pytdgl3d 0.3.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.
- pytdgl3d-0.3.0.dist-info/METADATA +1268 -0
- pytdgl3d-0.3.0.dist-info/RECORD +59 -0
- pytdgl3d-0.3.0.dist-info/WHEEL +5 -0
- pytdgl3d-0.3.0.dist-info/entry_points.txt +2 -0
- pytdgl3d-0.3.0.dist-info/licenses/LICENSE +21 -0
- pytdgl3d-0.3.0.dist-info/top_level.txt +1 -0
- tdgl3d/__init__.py +51 -0
- tdgl3d/about.py +81 -0
- tdgl3d/device/__init__.py +4 -0
- tdgl3d/device/device.py +544 -0
- tdgl3d/device/layer.py +83 -0
- tdgl3d/device/meshing.py +285 -0
- tdgl3d/device/volume.py +188 -0
- tdgl3d/distance.py +60 -0
- tdgl3d/em.py +180 -0
- tdgl3d/finite_volume/__init__.py +3 -0
- tdgl3d/finite_volume/edge_mesh.py +112 -0
- tdgl3d/finite_volume/mesh.py +271 -0
- tdgl3d/finite_volume/operators.py +337 -0
- tdgl3d/finite_volume/util.py +243 -0
- tdgl3d/fluxoid.py +215 -0
- tdgl3d/geometry.py +492 -0
- tdgl3d/parameter.py +258 -0
- tdgl3d/solution/__init__.py +20 -0
- tdgl3d/solution/data.py +288 -0
- tdgl3d/solution/plot_solution.py +595 -0
- tdgl3d/solution/solution.py +982 -0
- tdgl3d/solver/__init__.py +3 -0
- tdgl3d/solver/options.py +77 -0
- tdgl3d/solver/runner.py +265 -0
- tdgl3d/solver/screening.py +175 -0
- tdgl3d/solver/solve.py +105 -0
- tdgl3d/solver/solver.py +905 -0
- tdgl3d/sources/__init__.py +3 -0
- tdgl3d/sources/constant.py +32 -0
- tdgl3d/sources/loop.py +61 -0
- tdgl3d/sources/scaling.py +52 -0
- tdgl3d/test/__init__.py +0 -0
- tdgl3d/test/test_completeness.py +782 -0
- tdgl3d/test/test_cone.py +316 -0
- tdgl3d/test/test_fast.py +210 -0
- tdgl3d/test/test_features.py +384 -0
- tdgl3d/test/test_upgrades.py +499 -0
- tdgl3d/testing.py +19 -0
- tdgl3d/version.py +2 -0
- tdgl3d/visualization/__init__.py +53 -0
- tdgl3d/visualization/analysis.py +205 -0
- tdgl3d/visualization/animate.py +222 -0
- tdgl3d/visualization/common.py +219 -0
- tdgl3d/visualization/convert.py +73 -0
- tdgl3d/visualization/interactive.py +349 -0
- tdgl3d/visualization/io.py +103 -0
- tdgl3d/visualization/monitor.py +177 -0
- tdgl3d/visualization/physics.py +481 -0
- tdgl3d/visualization/plots3d.py +420 -0
- tdgl3d/visualization/publication.py +735 -0
- tdgl3d/visualization/snapshot.py +143 -0
- tdgl3d/visualization/vortex.py +679 -0
- tdgl3d/visualize.py +138 -0
|
@@ -0,0 +1,1268 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytdgl3d
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: 3D Time-Dependent Ginzburg-Landau solver
|
|
5
|
+
Author-email: Tanvir Hassan <tanvir6307@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.14
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: numpy>=2.4.2
|
|
11
|
+
Requires-Dist: scipy>=1.17.0
|
|
12
|
+
Requires-Dist: h5py>=3.15.1
|
|
13
|
+
Requires-Dist: numba>=0.64.0
|
|
14
|
+
Requires-Dist: pint>=0.25.2
|
|
15
|
+
Requires-Dist: cloudpickle>=3.1.2
|
|
16
|
+
Requires-Dist: matplotlib>=3.10.8
|
|
17
|
+
Requires-Dist: Pillow>=12.1.1
|
|
18
|
+
Provides-Extra: visualization
|
|
19
|
+
Requires-Dist: pyvista>=0.35; extra == "visualization"
|
|
20
|
+
Requires-Dist: meshio>=5.0; extra == "visualization"
|
|
21
|
+
Provides-Extra: gpu
|
|
22
|
+
Requires-Dist: cupy-cuda12x>=14.0.1; extra == "gpu"
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# tdgl3d
|
|
28
|
+
|
|
29
|
+
`tdgl3d` is an open-source Python package for solving three-dimensional
|
|
30
|
+
time-dependent Ginzburg-Landau (TDGL) problems in superconductors. It represents
|
|
31
|
+
the superconducting domain by a closed triangular surface mesh, generates a
|
|
32
|
+
tetrahedral volume mesh, assembles finite-volume operators on mesh vertices and
|
|
33
|
+
edges, and evolves the complex superconducting order parameter using
|
|
34
|
+
gauge-invariant link variables.
|
|
35
|
+
|
|
36
|
+
The package is designed for computational studies of vortex nucleation, vortex
|
|
37
|
+
motion, magnetic screening, fluxoid quantization, and transport-like boundary
|
|
38
|
+
conditions in finite three-dimensional geometries. Built-in geometry generators
|
|
39
|
+
cover common shapes such as cones, cylinders, cuboids, cubes, pyramids, spheres,
|
|
40
|
+
and ellipsoids, while user-defined closed surface meshes can also be supplied.
|
|
41
|
+
|
|
42
|
+
## Contents
|
|
43
|
+
|
|
44
|
+
- [Scientific Scope](#scientific-scope)
|
|
45
|
+
- [Main Features](#main-features)
|
|
46
|
+
- [Numerical Method](#numerical-method)
|
|
47
|
+
- [Package Architecture](#package-architecture)
|
|
48
|
+
- [Installation](#installation)
|
|
49
|
+
- [Quick Start](#quick-start)
|
|
50
|
+
- [Geometry and Device Construction](#geometry-and-device-construction)
|
|
51
|
+
- [Solver Usage](#solver-usage)
|
|
52
|
+
- [Magnetic Sources](#magnetic-sources)
|
|
53
|
+
- [Screening and GPU Acceleration](#screening-and-gpu-acceleration)
|
|
54
|
+
- [Terminal Currents and Probe Points](#terminal-currents-and-probe-points)
|
|
55
|
+
- [Solution Analysis](#solution-analysis)
|
|
56
|
+
- [Visualization](#visualization)
|
|
57
|
+
- [HDF5 Output Format](#hdf5-output-format)
|
|
58
|
+
- [Testing and Validation](#testing-and-validation)
|
|
59
|
+
- [Reproducibility Checklist](#reproducibility-checklist)
|
|
60
|
+
- [Known Limitations](#known-limitations)
|
|
61
|
+
- [License](#license)
|
|
62
|
+
- [Citation](#citation)
|
|
63
|
+
|
|
64
|
+
## Scientific Scope
|
|
65
|
+
|
|
66
|
+
`tdgl3d` solves a generalized TDGL model for a superconducting volume
|
|
67
|
+
`Omega`. The primary fields are:
|
|
68
|
+
|
|
69
|
+
- `psi(r, t)`: complex superconducting order parameter.
|
|
70
|
+
- `mu(r, t)`: scalar electric potential.
|
|
71
|
+
- `A(r, t)`: vector potential.
|
|
72
|
+
- `J_s`: superconducting current on mesh edges.
|
|
73
|
+
- `J_n`: normal current on mesh edges.
|
|
74
|
+
|
|
75
|
+
The code uses dimensionless TDGL units internally. Lengths are scaled by the
|
|
76
|
+
coherence length `xi`, magnetic fields by `Bc2`, vector potentials by
|
|
77
|
+
`xi * Bc2`, and currents by the corresponding GL current scale exposed through
|
|
78
|
+
the `Device` physical-scale helpers.
|
|
79
|
+
|
|
80
|
+
The intended use cases include:
|
|
81
|
+
|
|
82
|
+
- 3D Abrikosov vortex nucleation and relaxation.
|
|
83
|
+
- Vortex line deformation in non-planar geometries.
|
|
84
|
+
- Surface and geometry effects in cones, tips, spheres, and finite-thickness
|
|
85
|
+
mesoscopic superconductors.
|
|
86
|
+
- Fluxoid and winding-number analysis along arbitrary 3D contours.
|
|
87
|
+
- Transport-style simulations using terminal volumes and scalar-potential
|
|
88
|
+
boundary feedback.
|
|
89
|
+
- Post-processing of HDF5 TDGL trajectories for visualization and physical
|
|
90
|
+
observables.
|
|
91
|
+
|
|
92
|
+
## Main Features
|
|
93
|
+
|
|
94
|
+
- 3D tetrahedral finite-volume mesh support.
|
|
95
|
+
- Gauge-invariant edge link variables for vector-potential coupling.
|
|
96
|
+
- Adaptive semi-implicit Euler time stepping for TDGL evolution.
|
|
97
|
+
- Built-in geometry generators:
|
|
98
|
+
`cone`, `cylinder`, `cuboid`, `box`, `cube`, `pyramid`, `sphere`,
|
|
99
|
+
`ellipsoid`.
|
|
100
|
+
- User-supplied closed triangular surface meshes through `Volume`.
|
|
101
|
+
- Material model through `Layer`, including `xi`, London penetration depth,
|
|
102
|
+
conductivity, `u`, and `gamma`.
|
|
103
|
+
- `Device` abstraction for geometry, materials, mesh generation, terminal
|
|
104
|
+
volumes, probe points, transformations, HDF5 I/O, and physical scales.
|
|
105
|
+
- Sparse finite-volume gradient, divergence, and Laplacian operators.
|
|
106
|
+
- Optional magnetic self-field screening using a 3D Biot-Savart summation.
|
|
107
|
+
- Numba-accelerated CPU screening kernel.
|
|
108
|
+
- Optional CuPy CUDA screening kernel.
|
|
109
|
+
- Uniform magnetic field and circular current-loop source helpers.
|
|
110
|
+
- Spatially and time-dependent parameters using Python callables.
|
|
111
|
+
- Seed-solution support for continuation studies.
|
|
112
|
+
- HDF5 output containing device, mesh, options, snapshots, and dynamics.
|
|
113
|
+
- Solution analysis for order parameter, phase, current density, magnetic
|
|
114
|
+
moment, magnetic field, vorticity, superfluid velocity, London penetration
|
|
115
|
+
depth, and fluxoid quantization.
|
|
116
|
+
- Matplotlib slice plots, 3D plotting helpers, snapshots, GIF/MP4 animation,
|
|
117
|
+
live monitoring, vortex detection, and XDMF export.
|
|
118
|
+
- Test suite covering geometry, meshing, finite-volume operators, short solver
|
|
119
|
+
runs, serialization, visualization, screening, and post-processing.
|
|
120
|
+
|
|
121
|
+
## Numerical Method
|
|
122
|
+
|
|
123
|
+
### Mesh and Control Volumes
|
|
124
|
+
|
|
125
|
+
The computational domain is a closed triangular surface mesh. `tdgl3d` fills
|
|
126
|
+
the interior with points and uses SciPy Delaunay tetrahedralization to create a
|
|
127
|
+
tetrahedral mesh. Tetrahedra with centroids outside the surface are discarded,
|
|
128
|
+
and boundary vertices are projected back to the original surface.
|
|
129
|
+
|
|
130
|
+
For each mesh vertex, a barycentric control volume is approximated by
|
|
131
|
+
distributing each tetrahedron volume equally among its four vertices. The mesh
|
|
132
|
+
stores:
|
|
133
|
+
|
|
134
|
+
- `sites`: vertex coordinates in dimensionless units.
|
|
135
|
+
- `elements`: tetrahedral connectivity.
|
|
136
|
+
- `boundary_indices`: mesh sites on the boundary.
|
|
137
|
+
- `volumes`: per-site control volumes.
|
|
138
|
+
- `edge_mesh`: unique edges, edge centers, edge directions, edge lengths,
|
|
139
|
+
approximate dual-face areas, and boundary-edge indices.
|
|
140
|
+
|
|
141
|
+
### Finite-Volume Operators
|
|
142
|
+
|
|
143
|
+
The finite-volume operators are assembled on the edge mesh:
|
|
144
|
+
|
|
145
|
+
- `build_gradient`: edge gradient from site values to edge values.
|
|
146
|
+
- `build_divergence`: site divergence from edge values.
|
|
147
|
+
- `build_laplacian`: finite-volume Laplacian assembled from edge weights.
|
|
148
|
+
- `MeshOperators`: cached divergence, scalar Laplacian, covariant gradient,
|
|
149
|
+
and covariant Laplacian.
|
|
150
|
+
|
|
151
|
+
The edge weights use edge lengths, site control volumes, and approximate
|
|
152
|
+
dual-face areas. This gives a practical Delaunay-Voronoi-style discretization
|
|
153
|
+
for 3D exploratory calculations.
|
|
154
|
+
|
|
155
|
+
### Gauge-Invariant Link Variables
|
|
156
|
+
|
|
157
|
+
The vector potential is evaluated at edge midpoints. For each oriented edge
|
|
158
|
+
`i -> j`, the code computes a link variable
|
|
159
|
+
|
|
160
|
+
```text
|
|
161
|
+
U_ij = exp(-i A_ij . (r_j - r_i))
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The covariant gradient uses the link variable:
|
|
165
|
+
|
|
166
|
+
```text
|
|
167
|
+
grad_A(psi)_ij = (U_ij psi_j - psi_i) / L_ij
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
This preserves gauge-invariant phase coupling on the mesh edges.
|
|
171
|
+
|
|
172
|
+
### Time Stepping
|
|
173
|
+
|
|
174
|
+
The TDGL update uses a semi-implicit Euler step. The nonlinear term in
|
|
175
|
+
`|psi|^2` is handled through a quadratic solve for the next order-parameter
|
|
176
|
+
magnitude. Adaptive stepping reduces the time step on failed updates and uses a
|
|
177
|
+
sliding-window estimate of recent `|psi|^2` changes to adjust the next step.
|
|
178
|
+
|
|
179
|
+
At each successful step, the solver:
|
|
180
|
+
|
|
181
|
+
1. Updates time-dependent disorder or pair-breaking fields.
|
|
182
|
+
2. Advances `psi`.
|
|
183
|
+
3. Solves for scalar potential `mu`.
|
|
184
|
+
4. Computes supercurrent and normal current.
|
|
185
|
+
5. Applies terminal-current feedback when terminals are configured.
|
|
186
|
+
6. Updates the induced vector potential if screening is enabled.
|
|
187
|
+
7. Periodically applies a Coulomb-gauge projection to the induced field.
|
|
188
|
+
8. Saves snapshots according to `save_every`.
|
|
189
|
+
|
|
190
|
+
### Screening
|
|
191
|
+
|
|
192
|
+
When `include_screening=True`, the induced vector potential is updated from the
|
|
193
|
+
site current density using a Biot-Savart-type volume summation:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
A_induced(r_e) ~ sum_j J(r_j) V_j / |r_e - r_j|
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The screening update uses a Polyak heavy-ball iteration controlled in the
|
|
200
|
+
current implementation by `screening_step_size` and `screening_drag`.
|
|
201
|
+
`screening_tolerance` is stored in the solver options as a screening
|
|
202
|
+
convergence parameter. The CPU implementation uses Numba. The optional GPU path
|
|
203
|
+
uses a CuPy raw CUDA kernel.
|
|
204
|
+
|
|
205
|
+
## Package Architecture
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
tdgl3d/
|
|
209
|
+
__init__.py Public API exports
|
|
210
|
+
about.py Version and dependency table helpers
|
|
211
|
+
em.py Unit registry, vector potentials, Biot-Savart tools
|
|
212
|
+
fluxoid.py 3D fluxoid contours and fluxoid integration
|
|
213
|
+
geometry.py Surface mesh generators
|
|
214
|
+
parameter.py Callable spatial/time-dependent parameters
|
|
215
|
+
testing.py Test runner helper
|
|
216
|
+
|
|
217
|
+
device/
|
|
218
|
+
layer.py Material parameters
|
|
219
|
+
volume.py Closed triangular surface mesh container
|
|
220
|
+
meshing.py Interior fill and tetrahedral mesh generation
|
|
221
|
+
device.py Device abstraction and HDF5 I/O
|
|
222
|
+
|
|
223
|
+
finite_volume/
|
|
224
|
+
mesh.py Tetrahedral mesh and mesh quality metrics
|
|
225
|
+
edge_mesh.py Edge topology and edge geometry
|
|
226
|
+
operators.py Gradient, divergence, Laplacian, link variables
|
|
227
|
+
util.py Mesh topology and geometry utilities
|
|
228
|
+
|
|
229
|
+
solver/
|
|
230
|
+
options.py SolverOptions dataclass
|
|
231
|
+
solve.py Public solve() function and terminal validation
|
|
232
|
+
solver.py TDGLSolver implementation
|
|
233
|
+
runner.py Simulation loop and HDF5 writer
|
|
234
|
+
screening.py Numba/CuPy induced-vector-potential kernels
|
|
235
|
+
|
|
236
|
+
solution/
|
|
237
|
+
data.py TDGLData, DynamicsData, HDF5 data helpers
|
|
238
|
+
solution.py Solution object and analysis methods
|
|
239
|
+
plot_solution.py Solution-bound Matplotlib plotting helpers
|
|
240
|
+
|
|
241
|
+
sources/
|
|
242
|
+
constant.py Uniform-field source
|
|
243
|
+
loop.py Circular current-loop vector potential
|
|
244
|
+
scaling.py LinearRamp and Scale parameters
|
|
245
|
+
|
|
246
|
+
visualization/
|
|
247
|
+
common.py Quantity enum, plot defaults, utility helpers
|
|
248
|
+
io.py HDF5 plotting data extraction
|
|
249
|
+
snapshot.py Snapshot generation
|
|
250
|
+
animate.py Animation generation
|
|
251
|
+
interactive.py Interactive plotting helpers
|
|
252
|
+
convert.py XDMF export
|
|
253
|
+
monitor.py Live monitoring
|
|
254
|
+
plots3d.py 3D plotting helpers
|
|
255
|
+
vortex.py Vortex detection and vortex-tube rendering
|
|
256
|
+
analysis.py Supercurrent and multi-slice analysis panels
|
|
257
|
+
physics.py Voltage, V-I, free-energy, and Lorentz plots
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Installation
|
|
261
|
+
|
|
262
|
+
### Requirements
|
|
263
|
+
|
|
264
|
+
The package metadata targets:
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
Python >= 3.14
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Core Python dependencies:
|
|
271
|
+
|
|
272
|
+
- NumPy
|
|
273
|
+
- SciPy
|
|
274
|
+
- h5py
|
|
275
|
+
- Numba
|
|
276
|
+
- Pint
|
|
277
|
+
- cloudpickle
|
|
278
|
+
- Matplotlib
|
|
279
|
+
- Pillow
|
|
280
|
+
|
|
281
|
+
Optional dependencies:
|
|
282
|
+
|
|
283
|
+
- CuPy for CUDA GPU screening.
|
|
284
|
+
- PyVista for 3D visualization.
|
|
285
|
+
- meshio for XDMF export.
|
|
286
|
+
- pytest for development and testing.
|
|
287
|
+
|
|
288
|
+
### Install From a Local Checkout
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
pip install .
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Editable Development Install
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
pip install -e ".[dev,visualization]"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Install From requirements.txt
|
|
301
|
+
|
|
302
|
+
`requirements.txt` includes the optional packages listed in this repository:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
pip install -r requirements.txt
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Optional GPU Package
|
|
309
|
+
|
|
310
|
+
Install a CuPy package matching your CUDA version. For CUDA 12.x:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
pip install cupy-cuda12x
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
For CUDA 11.x:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
pip install cupy-cuda11x
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Quick Start
|
|
323
|
+
|
|
324
|
+
This example builds a cone-shaped superconductor, generates a tetrahedral
|
|
325
|
+
mesh, solves a short TDGL run with a uniform dimensionless `Bz` field, and
|
|
326
|
+
plots the final order-parameter magnitude on a cross-section.
|
|
327
|
+
|
|
328
|
+
```python
|
|
329
|
+
import matplotlib.pyplot as plt
|
|
330
|
+
import tdgl3d
|
|
331
|
+
|
|
332
|
+
layer = tdgl3d.Layer(
|
|
333
|
+
coherence_length=1.0,
|
|
334
|
+
london_lambda=2.0,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
vertices, facets = tdgl3d.cone(
|
|
338
|
+
radius_bottom=2.0,
|
|
339
|
+
radius_top=0.3,
|
|
340
|
+
height=3.0,
|
|
341
|
+
n_radial=24,
|
|
342
|
+
n_height=8,
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
volume = tdgl3d.Volume("cone", vertices, facets)
|
|
346
|
+
|
|
347
|
+
device = tdgl3d.Device(
|
|
348
|
+
name="cone_device",
|
|
349
|
+
layer=layer,
|
|
350
|
+
volume=volume,
|
|
351
|
+
length_units="um",
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
device.make_mesh(max_edge_length=0.9, min_points=150)
|
|
355
|
+
print(device.mesh_stats())
|
|
356
|
+
|
|
357
|
+
options = tdgl3d.SolverOptions(
|
|
358
|
+
solve_time=2.0,
|
|
359
|
+
dt_init=1e-3,
|
|
360
|
+
dt_max=5e-2,
|
|
361
|
+
adaptive=True,
|
|
362
|
+
save_every=50,
|
|
363
|
+
output_file="cone_solution.h5",
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
solution = tdgl3d.solve(
|
|
367
|
+
device=device,
|
|
368
|
+
options=options,
|
|
369
|
+
applied_vector_potential=0.3,
|
|
370
|
+
seed_noise=0.01,
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
fig, ax = solution.plot_order_parameter(slice_axis="z")
|
|
374
|
+
plt.show()
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## Geometry and Device Construction
|
|
378
|
+
|
|
379
|
+
### Built-In Geometry Generators
|
|
380
|
+
|
|
381
|
+
Each geometry function returns:
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
vertices, facets = tdgl3d.geometry_function(...)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
where `vertices` has shape `(N, 3)` and `facets` has shape `(M, 3)`.
|
|
388
|
+
|
|
389
|
+
| Function | Purpose |
|
|
390
|
+
| --- | --- |
|
|
391
|
+
| `cone(radius_bottom, radius_top, height)` | Cone or truncated cone aligned with `z` |
|
|
392
|
+
| `cylinder(radius, height)` | Cylinder, implemented as a cone special case |
|
|
393
|
+
| `cuboid(lx, ly, lz)` | Rectangular box |
|
|
394
|
+
| `box(lx, ly, lz)` | Alias-style box generator |
|
|
395
|
+
| `cube(side)` | Cube |
|
|
396
|
+
| `pyramid(...)` | Pyramid with polygonal or rectangular base |
|
|
397
|
+
| `sphere(radius)` | Spherical surface mesh |
|
|
398
|
+
| `ellipsoid(a, b, c)` | Axis-aligned ellipsoid |
|
|
399
|
+
|
|
400
|
+
Example:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
vertices, facets = tdgl3d.sphere(radius=2.0, n_phi=30, n_theta=60)
|
|
404
|
+
tdgl3d.close_surface(vertices, facets)
|
|
405
|
+
volume = tdgl3d.Volume("sphere", vertices, facets)
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Custom Closed Surface Meshes
|
|
409
|
+
|
|
410
|
+
You may construct a `Volume` from your own closed triangular surface mesh:
|
|
411
|
+
|
|
412
|
+
```python
|
|
413
|
+
volume = tdgl3d.Volume(
|
|
414
|
+
name="custom",
|
|
415
|
+
vertices=vertices_array,
|
|
416
|
+
facets=triangles_array,
|
|
417
|
+
)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
The surface must be watertight. A closed triangular mesh should have each
|
|
421
|
+
surface edge shared by exactly two triangles. Use `close_surface()` to check
|
|
422
|
+
simple watertightness before meshing.
|
|
423
|
+
|
|
424
|
+
### Device Transformations
|
|
425
|
+
|
|
426
|
+
`Volume` and `Device` support basic transformations:
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
device_shifted = device.translate(dx=1.0, dy=0.0, dz=0.5)
|
|
430
|
+
device_scaled = device.scale(sx=1.0, sy=1.0, sz=2.0)
|
|
431
|
+
device_rotated = device.rotate(angle=45.0, axis="z")
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Transformed devices do not keep the old mesh; regenerate the mesh after
|
|
435
|
+
changing geometry.
|
|
436
|
+
|
|
437
|
+
### Mesh Generation
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
device.make_mesh(max_edge_length=1.0, min_points=300)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Parameters:
|
|
444
|
+
|
|
445
|
+
- `max_edge_length`: target mesh spacing in the device length units.
|
|
446
|
+
- `min_points`: minimum interior point target used during point filling.
|
|
447
|
+
|
|
448
|
+
Inspect the generated mesh:
|
|
449
|
+
|
|
450
|
+
```python
|
|
451
|
+
stats = device.mesh_stats()
|
|
452
|
+
quality = device.mesh.quality_metrics()
|
|
453
|
+
print(stats)
|
|
454
|
+
print(quality["summary"])
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Plot the surface projection or filled surface projection:
|
|
458
|
+
|
|
459
|
+
```python
|
|
460
|
+
device.plot(mesh=True)
|
|
461
|
+
device.draw(alpha=0.5)
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Physical Scales
|
|
465
|
+
|
|
466
|
+
`Device` exposes useful dimensional scales through Pint:
|
|
467
|
+
|
|
468
|
+
```python
|
|
469
|
+
print(device.coherence_length)
|
|
470
|
+
print(device.Bc2)
|
|
471
|
+
print(device.A0)
|
|
472
|
+
print(device.K0)
|
|
473
|
+
print(device.tau0(conductivity=1e7))
|
|
474
|
+
print(device.V0(conductivity=1e7))
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Solver Usage
|
|
478
|
+
|
|
479
|
+
### SolverOptions
|
|
480
|
+
|
|
481
|
+
`SolverOptions` controls integration, output, screening, sparse solver choice,
|
|
482
|
+
GPU use, and monitoring.
|
|
483
|
+
|
|
484
|
+
| Option | Default | Meaning |
|
|
485
|
+
| --- | --- | --- |
|
|
486
|
+
| `solve_time` | `100.0` | Simulation time after optional skip period |
|
|
487
|
+
| `skip_time` | `0.0` | Initial time not saved to snapshots |
|
|
488
|
+
| `dt_init` | `1e-4` | Initial time step |
|
|
489
|
+
| `dt_max` | `1e-2` | Maximum adaptive time step |
|
|
490
|
+
| `adaptive` | `True` | Enable adaptive time stepping |
|
|
491
|
+
| `adaptive_window` | `10` | Window for time-step adaptation |
|
|
492
|
+
| `adaptive_time_step_multiplier` | `0.25` | Retry multiplier after failed step |
|
|
493
|
+
| `max_solve_retries` | `10` | Maximum retries per step |
|
|
494
|
+
| `output_file` | `None` | HDF5 output path; temporary file if unset |
|
|
495
|
+
| `field_units` | `"mT"` | Metadata field units |
|
|
496
|
+
| `current_units` | `"uA"` | Metadata current units |
|
|
497
|
+
| `save_every` | `100` | Save every N solver steps |
|
|
498
|
+
| `include_screening` | `False` | Enable magnetic self-field iteration |
|
|
499
|
+
| `screening_step_size` | `0.1` | Polyak screening step size |
|
|
500
|
+
| `screening_tolerance` | `1e-3` | Stored screening convergence parameter |
|
|
501
|
+
| `screening_drag` | `0.5` | Polyak heavy-ball drag |
|
|
502
|
+
| `terminal_psi` | `0.0` | `|psi|` imposed at terminal sites |
|
|
503
|
+
| `sparse_solver` | `"superlu"` | Sparse backend name |
|
|
504
|
+
| `gpu` | `False` | Use CuPy screening kernel |
|
|
505
|
+
| `monitor` | `False` | Enable SWMR-compatible live monitoring |
|
|
506
|
+
| `monitor_update_interval` | `2.0` | Monitor refresh interval |
|
|
507
|
+
| `pause_on_interrupt` | `True` | Save current state on keyboard interrupt |
|
|
508
|
+
|
|
509
|
+
Example:
|
|
510
|
+
|
|
511
|
+
```python
|
|
512
|
+
options = tdgl3d.SolverOptions(
|
|
513
|
+
solve_time=10.0,
|
|
514
|
+
skip_time=1.0,
|
|
515
|
+
dt_init=1e-3,
|
|
516
|
+
dt_max=5e-2,
|
|
517
|
+
adaptive=True,
|
|
518
|
+
save_every=100,
|
|
519
|
+
output_file="run.h5",
|
|
520
|
+
)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Applied Vector Potential
|
|
524
|
+
|
|
525
|
+
`applied_vector_potential` may be:
|
|
526
|
+
|
|
527
|
+
- `None` or `0`: no applied vector potential.
|
|
528
|
+
- `float`: interpreted as a uniform `Bz` field.
|
|
529
|
+
- `numpy.ndarray` with shape `(num_edges, 3)`: value at edge midpoints.
|
|
530
|
+
- Callable or `Parameter`: evaluated as `A(x, y, z)`.
|
|
531
|
+
|
|
532
|
+
Uniform field:
|
|
533
|
+
|
|
534
|
+
```python
|
|
535
|
+
solution = tdgl3d.solve(device, options, applied_vector_potential=0.3)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Callable field:
|
|
539
|
+
|
|
540
|
+
```python
|
|
541
|
+
def A_custom(x, y, z):
|
|
542
|
+
import numpy as np
|
|
543
|
+
A = np.zeros((len(x), 3))
|
|
544
|
+
A[:, 0] = -0.5 * y
|
|
545
|
+
A[:, 1] = 0.5 * x
|
|
546
|
+
return A
|
|
547
|
+
|
|
548
|
+
solution = tdgl3d.solve(device, options, applied_vector_potential=A_custom)
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Disorder or Pair-Breaking Parameter
|
|
552
|
+
|
|
553
|
+
`disorder_epsilon` can be a scalar, array, spatial callable, or time-dependent
|
|
554
|
+
callable. Time-dependent functions must accept keyword-only `t`.
|
|
555
|
+
|
|
556
|
+
```python
|
|
557
|
+
def epsilon(x, y, z, *, t):
|
|
558
|
+
import numpy as np
|
|
559
|
+
value = max(1.0 - 0.1 * t, 0.5)
|
|
560
|
+
return value * np.ones_like(x)
|
|
561
|
+
|
|
562
|
+
solution = tdgl3d.solve(
|
|
563
|
+
device,
|
|
564
|
+
options,
|
|
565
|
+
disorder_epsilon=epsilon,
|
|
566
|
+
)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Initial Conditions
|
|
570
|
+
|
|
571
|
+
Initial-condition controls:
|
|
572
|
+
|
|
573
|
+
- `seed_noise`: small complex noise added to the initial state.
|
|
574
|
+
- `field_cooling=True`: initialize a moderate-amplitude random-phase state to
|
|
575
|
+
help vortex nucleation.
|
|
576
|
+
- `seed_solution`: initialize from a previous `Solution`; different meshes are
|
|
577
|
+
handled by interpolation.
|
|
578
|
+
|
|
579
|
+
```python
|
|
580
|
+
solution2 = tdgl3d.solve(
|
|
581
|
+
device,
|
|
582
|
+
options,
|
|
583
|
+
applied_vector_potential=0.4,
|
|
584
|
+
seed_solution=solution1,
|
|
585
|
+
)
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Manual Solver Access
|
|
589
|
+
|
|
590
|
+
For advanced workflows:
|
|
591
|
+
|
|
592
|
+
```python
|
|
593
|
+
from tdgl3d.solver import TDGLSolver
|
|
594
|
+
|
|
595
|
+
solver = TDGLSolver(
|
|
596
|
+
device=device,
|
|
597
|
+
options=options,
|
|
598
|
+
applied_vector_potential=0.3,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
for _ in range(100):
|
|
602
|
+
dt, converged = solver.update()
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Most users should prefer `tdgl3d.solve()`.
|
|
606
|
+
|
|
607
|
+
## Magnetic Sources
|
|
608
|
+
|
|
609
|
+
### Uniform Field Source
|
|
610
|
+
|
|
611
|
+
```python
|
|
612
|
+
A = tdgl3d.sources.ConstantField(Bz=0.5)
|
|
613
|
+
solution = tdgl3d.solve(device, options, applied_vector_potential=A)
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
The source uses the symmetric gauge:
|
|
617
|
+
|
|
618
|
+
```text
|
|
619
|
+
A = (Bz / 2) (-y, x, 0)
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Circular Current Loop
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
loop = tdgl3d.sources.CurrentLoop(
|
|
626
|
+
current=1.0,
|
|
627
|
+
radius=5.0,
|
|
628
|
+
center=(0.0, 0.0, 0.0),
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
solution = tdgl3d.solve(device, options, applied_vector_potential=loop)
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Time-Dependent Scaling
|
|
635
|
+
|
|
636
|
+
```python
|
|
637
|
+
ramp = tdgl3d.sources.LinearRamp(
|
|
638
|
+
tmin=0.0,
|
|
639
|
+
tmax=10.0,
|
|
640
|
+
vmin=0.0,
|
|
641
|
+
vmax=1.0,
|
|
642
|
+
)
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
Custom `Parameter` objects can be combined arithmetically:
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
param = 0.5 * tdgl3d.sources.Scale(2.0)
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## Screening and GPU Acceleration
|
|
652
|
+
|
|
653
|
+
Enable magnetic self-field screening:
|
|
654
|
+
|
|
655
|
+
```python
|
|
656
|
+
options = tdgl3d.SolverOptions(
|
|
657
|
+
solve_time=5.0,
|
|
658
|
+
include_screening=True,
|
|
659
|
+
screening_step_size=0.1,
|
|
660
|
+
screening_drag=0.5,
|
|
661
|
+
gpu=False,
|
|
662
|
+
)
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
Use the GPU kernel when CuPy is installed:
|
|
666
|
+
|
|
667
|
+
```python
|
|
668
|
+
options.gpu = True
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
Notes:
|
|
672
|
+
|
|
673
|
+
- The screening calculation is typically the most expensive part of a run.
|
|
674
|
+
- The CPU path is Numba-accelerated and may compile on first use.
|
|
675
|
+
- The GPU path requires a compatible CUDA runtime and a matching CuPy package.
|
|
676
|
+
- Very large meshes can exceed GPU memory because the kernel evaluates many
|
|
677
|
+
source-site and target-edge interactions.
|
|
678
|
+
|
|
679
|
+
## Terminal Currents and Probe Points
|
|
680
|
+
|
|
681
|
+
### Terminal Volumes
|
|
682
|
+
|
|
683
|
+
Terminals are represented as `Volume` objects associated with a `Device`.
|
|
684
|
+
The solver identifies mesh sites belonging to those terminal regions and uses
|
|
685
|
+
them for scalar-potential boundary feedback and optional `psi` suppression.
|
|
686
|
+
|
|
687
|
+
```python
|
|
688
|
+
terminal_left = tdgl3d.Volume("left", left_vertices, left_facets)
|
|
689
|
+
terminal_right = tdgl3d.Volume("right", right_vertices, right_facets)
|
|
690
|
+
|
|
691
|
+
device = tdgl3d.Device(
|
|
692
|
+
"wire",
|
|
693
|
+
layer=layer,
|
|
694
|
+
volume=body,
|
|
695
|
+
terminals=[terminal_left, terminal_right],
|
|
696
|
+
)
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
Inspect terminal metadata:
|
|
700
|
+
|
|
701
|
+
```python
|
|
702
|
+
print(device.terminal_info())
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
### Terminal Driving
|
|
706
|
+
|
|
707
|
+
The solver accepts `terminal_currents` in `tdgl3d.solve()` as either a
|
|
708
|
+
dictionary or a callable returning a dictionary. The implemented terminal
|
|
709
|
+
update distinguishes two value forms:
|
|
710
|
+
|
|
711
|
+
- Scalar value: direct scalar-potential bias at that terminal.
|
|
712
|
+
- `{"current": I}`: current-bias feedback that adjusts terminal `mu` to drive
|
|
713
|
+
the measured normal current toward `I`.
|
|
714
|
+
|
|
715
|
+
Voltage-bias style input:
|
|
716
|
+
|
|
717
|
+
```python
|
|
718
|
+
solution = tdgl3d.solve(
|
|
719
|
+
device,
|
|
720
|
+
options,
|
|
721
|
+
terminal_currents={"left": 0.5, "right": -0.5},
|
|
722
|
+
)
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Current-bias feedback:
|
|
726
|
+
|
|
727
|
+
```python
|
|
728
|
+
solution = tdgl3d.solve(
|
|
729
|
+
device,
|
|
730
|
+
options,
|
|
731
|
+
terminal_currents={
|
|
732
|
+
"left": {"current": 1.0},
|
|
733
|
+
"right": {"current": -1.0},
|
|
734
|
+
},
|
|
735
|
+
)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Time-dependent driving:
|
|
739
|
+
|
|
740
|
+
```python
|
|
741
|
+
def drive(t):
|
|
742
|
+
return {
|
|
743
|
+
"left": {"current": 1.0},
|
|
744
|
+
"right": {"current": -1.0},
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
solution = tdgl3d.solve(
|
|
748
|
+
device,
|
|
749
|
+
options,
|
|
750
|
+
terminal_currents=drive,
|
|
751
|
+
)
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### Current Conservation Check
|
|
755
|
+
|
|
756
|
+
Static currents:
|
|
757
|
+
|
|
758
|
+
```python
|
|
759
|
+
tdgl3d.validate_terminal_currents(
|
|
760
|
+
{"left": 1.0, "right": -1.0},
|
|
761
|
+
terminal_names=["left", "right"],
|
|
762
|
+
)
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Time-dependent currents:
|
|
766
|
+
|
|
767
|
+
```python
|
|
768
|
+
def currents(t):
|
|
769
|
+
return {"left": 1.0, "right": -1.0}
|
|
770
|
+
|
|
771
|
+
tdgl3d.validate_terminal_currents(
|
|
772
|
+
currents,
|
|
773
|
+
terminal_names=["left", "right"],
|
|
774
|
+
options=options,
|
|
775
|
+
)
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
`validate_terminal_currents()` currently validates flat scalar dictionaries or
|
|
779
|
+
callables returning flat scalar dictionaries. It checks that terminal names are
|
|
780
|
+
known and that the scalar values sum to zero. For nested current-bias
|
|
781
|
+
dictionaries such as `{"left": {"current": 1.0}}`, check conservation manually
|
|
782
|
+
before passing them to `solve()`.
|
|
783
|
+
|
|
784
|
+
The sum over all terminal currents must be zero.
|
|
785
|
+
|
|
786
|
+
### Probe Points
|
|
787
|
+
|
|
788
|
+
Probe points are physical coordinates used to record scalar potential during
|
|
789
|
+
the run:
|
|
790
|
+
|
|
791
|
+
```python
|
|
792
|
+
device = tdgl3d.Device(
|
|
793
|
+
"device_with_probes",
|
|
794
|
+
layer=layer,
|
|
795
|
+
volume=volume,
|
|
796
|
+
probe_points=[
|
|
797
|
+
[-1.0, 0.0, 0.5],
|
|
798
|
+
[1.0, 0.0, 0.5],
|
|
799
|
+
],
|
|
800
|
+
)
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
After solving:
|
|
804
|
+
|
|
805
|
+
```python
|
|
806
|
+
voltage = solution.dynamics.voltage(i=0, j=1)
|
|
807
|
+
mean_v = solution.dynamics.mean_voltage(i=0, j=1, tmin=1.0)
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
## Solution Analysis
|
|
811
|
+
|
|
812
|
+
Load a solution:
|
|
813
|
+
|
|
814
|
+
```python
|
|
815
|
+
solution = tdgl3d.Solution.from_hdf5("run.h5")
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
Common properties:
|
|
819
|
+
|
|
820
|
+
```python
|
|
821
|
+
psi = solution.order_parameter
|
|
822
|
+
abs_psi = solution.order_parameter_magnitude
|
|
823
|
+
phase = solution.phase
|
|
824
|
+
mu = solution.scalar_potential
|
|
825
|
+
Js = solution.supercurrent
|
|
826
|
+
Jn = solution.normal_current
|
|
827
|
+
times = solution.times
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
Select data by saved frame:
|
|
831
|
+
|
|
832
|
+
```python
|
|
833
|
+
data_range = solution.data_range
|
|
834
|
+
data0 = solution.get_data_at_step(data_range[0])
|
|
835
|
+
last = solution.get_data_at_step(data_range[1])
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
Find the closest saved frame to a target simulation time:
|
|
839
|
+
|
|
840
|
+
```python
|
|
841
|
+
frame = solution.closest_solve_step(target_time=5.0)
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### Derived Quantities
|
|
845
|
+
|
|
846
|
+
```python
|
|
847
|
+
magnetic_moment = solution.magnetic_moment()
|
|
848
|
+
magnetic_field = solution.magnetic_field()
|
|
849
|
+
free_energy = solution.free_energy()
|
|
850
|
+
superfluid_velocity = solution.superfluid_velocity()
|
|
851
|
+
lambda_eff = solution.london_penetration_depth()
|
|
852
|
+
vorticity = solution.vorticity
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Interpolation
|
|
856
|
+
|
|
857
|
+
Order parameter:
|
|
858
|
+
|
|
859
|
+
```python
|
|
860
|
+
positions = solution.mesh.sites[:10]
|
|
861
|
+
psi_at_positions = solution.interp_order_parameter(positions)
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
Current density:
|
|
865
|
+
|
|
866
|
+
```python
|
|
867
|
+
J = solution.interp_current_density(
|
|
868
|
+
positions,
|
|
869
|
+
dataset=None, # None = supercurrent + normal current
|
|
870
|
+
method="nearest",
|
|
871
|
+
)
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
Regular 3D grid:
|
|
875
|
+
|
|
876
|
+
```python
|
|
877
|
+
xg, yg, zg, Jgrid = solution.grid_current_density(
|
|
878
|
+
grid_shape=(40, 40, 40),
|
|
879
|
+
method="nearest",
|
|
880
|
+
)
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Fluxoid Quantization
|
|
884
|
+
|
|
885
|
+
Circular contour:
|
|
886
|
+
|
|
887
|
+
```python
|
|
888
|
+
contour = tdgl3d.make_fluxoid_contour_circle(
|
|
889
|
+
center=(0.0, 0.0, 1.0),
|
|
890
|
+
radius=0.5,
|
|
891
|
+
normal=(0.0, 0.0, 1.0),
|
|
892
|
+
n_points=64,
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
fluxoid = solution.fluxoid(contour, kappa=solution.device.kappa)
|
|
896
|
+
print(fluxoid.flux_part, fluxoid.supercurrent_part)
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
Validation helper:
|
|
900
|
+
|
|
901
|
+
```python
|
|
902
|
+
result = solution.validate_flux_quantization(
|
|
903
|
+
contour,
|
|
904
|
+
kappa=solution.device.kappa,
|
|
905
|
+
tolerance=0.1,
|
|
906
|
+
)
|
|
907
|
+
print(result)
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Rectangular contour:
|
|
911
|
+
|
|
912
|
+
```python
|
|
913
|
+
contour = tdgl3d.make_fluxoid_contour_rectangular(
|
|
914
|
+
center=(0.0, 0.0, 1.0),
|
|
915
|
+
width=1.0,
|
|
916
|
+
height=1.0,
|
|
917
|
+
normal=(0.0, 0.0, 1.0),
|
|
918
|
+
n_points_per_side=16,
|
|
919
|
+
)
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
### Current Through a Surface
|
|
923
|
+
|
|
924
|
+
For a sampled planar surface:
|
|
925
|
+
|
|
926
|
+
```python
|
|
927
|
+
current = solution.current_through_surface(
|
|
928
|
+
surface_points=points_on_surface,
|
|
929
|
+
surface_normal=[1.0, 0.0, 0.0],
|
|
930
|
+
)
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
For time series through named paths:
|
|
934
|
+
|
|
935
|
+
```python
|
|
936
|
+
from tdgl3d.solution.data import get_current_through_paths
|
|
937
|
+
|
|
938
|
+
paths = {"center": points_on_surface}
|
|
939
|
+
currents = get_current_through_paths("run.h5", paths)
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
## Visualization
|
|
943
|
+
|
|
944
|
+
### Solution Plot Shortcuts
|
|
945
|
+
|
|
946
|
+
Each plotting method returns `(fig, ax)`:
|
|
947
|
+
|
|
948
|
+
```python
|
|
949
|
+
solution.plot_order_parameter(slice_axis="z")
|
|
950
|
+
solution.plot_phase(slice_axis="z")
|
|
951
|
+
solution.plot_scalar_potential(slice_axis="z")
|
|
952
|
+
solution.plot_supercurrent(slice_axis="z")
|
|
953
|
+
solution.plot_currents(slice_axis="z")
|
|
954
|
+
solution.plot_vorticity(slice_axis="z")
|
|
955
|
+
solution.plot_vector_potential(slice_axis="z")
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
Slice options:
|
|
959
|
+
|
|
960
|
+
- `slice_axis`: `"x"`, `"y"`, or `"z"`.
|
|
961
|
+
- `slice_value`: coordinate of the slice. If omitted, the midpoint is used.
|
|
962
|
+
|
|
963
|
+
### Snapshot Generation
|
|
964
|
+
|
|
965
|
+
```python
|
|
966
|
+
from tdgl3d.visualization import generate_snapshots
|
|
967
|
+
|
|
968
|
+
figures = generate_snapshots(
|
|
969
|
+
input_path="run.h5",
|
|
970
|
+
steps=[0, 5, 10],
|
|
971
|
+
quantities=["order_parameter", "phase", "supercurrent"],
|
|
972
|
+
slice_axis="z",
|
|
973
|
+
)
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
### Animation
|
|
977
|
+
|
|
978
|
+
```python
|
|
979
|
+
from tdgl3d.visualization import create_animation
|
|
980
|
+
|
|
981
|
+
create_animation(
|
|
982
|
+
input_file="run.h5",
|
|
983
|
+
output_file="order_parameter.gif",
|
|
984
|
+
quantity="order_parameter",
|
|
985
|
+
fps=10,
|
|
986
|
+
ngrid=80,
|
|
987
|
+
)
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
For MP4 output, Matplotlib needs a working `ffmpeg` writer.
|
|
991
|
+
|
|
992
|
+
### Command-Line Interface
|
|
993
|
+
|
|
994
|
+
The package defines the console script:
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
tdgl3d-visualize
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Examples:
|
|
1001
|
+
|
|
1002
|
+
```bash
|
|
1003
|
+
tdgl3d-visualize -i run.h5 snapshot --steps 0 5 10
|
|
1004
|
+
tdgl3d-visualize -i run.h5 interactive
|
|
1005
|
+
tdgl3d-visualize -i run.h5 -o solution.xdmf convert
|
|
1006
|
+
tdgl3d-visualize -i run.h5 monitor
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### 3D and Vortex Utilities
|
|
1010
|
+
|
|
1011
|
+
The visualization package also includes:
|
|
1012
|
+
|
|
1013
|
+
- `plot_3d_order_parameter`
|
|
1014
|
+
- `plot_3d_scalar_potential`
|
|
1015
|
+
- `plot_3d_vortex_cores`
|
|
1016
|
+
- `plot_3d_volume`
|
|
1017
|
+
- `plot_3d_isosurface`
|
|
1018
|
+
- `detect_vortex_positions`
|
|
1019
|
+
- `build_core_tubes`
|
|
1020
|
+
- `make_vortex_slice_gif`
|
|
1021
|
+
- `make_vortex_3d_gif`
|
|
1022
|
+
- `plot_supercurrent_analysis`
|
|
1023
|
+
- `plot_multi_z_slices`
|
|
1024
|
+
- `plot_voltage_vs_time`
|
|
1025
|
+
- `plot_vi_curve`
|
|
1026
|
+
- `plot_free_energy_density`
|
|
1027
|
+
- `plot_total_free_energy_vs_time`
|
|
1028
|
+
|
|
1029
|
+
## HDF5 Output Format
|
|
1030
|
+
|
|
1031
|
+
The solver writes an HDF5 file containing the full state required for
|
|
1032
|
+
post-processing.
|
|
1033
|
+
|
|
1034
|
+
```text
|
|
1035
|
+
/
|
|
1036
|
+
attrs:
|
|
1037
|
+
solver
|
|
1038
|
+
version
|
|
1039
|
+
field_units
|
|
1040
|
+
current_units
|
|
1041
|
+
total_steps
|
|
1042
|
+
total_saves
|
|
1043
|
+
|
|
1044
|
+
device/
|
|
1045
|
+
attrs:
|
|
1046
|
+
name
|
|
1047
|
+
length_units
|
|
1048
|
+
layer/
|
|
1049
|
+
attrs:
|
|
1050
|
+
coherence_length
|
|
1051
|
+
london_lambda
|
|
1052
|
+
thickness
|
|
1053
|
+
conductivity
|
|
1054
|
+
u
|
|
1055
|
+
gamma
|
|
1056
|
+
volume/
|
|
1057
|
+
attrs:
|
|
1058
|
+
name
|
|
1059
|
+
vertices
|
|
1060
|
+
facets
|
|
1061
|
+
holes/
|
|
1062
|
+
terminals/
|
|
1063
|
+
probe_points
|
|
1064
|
+
|
|
1065
|
+
mesh/
|
|
1066
|
+
sites
|
|
1067
|
+
elements
|
|
1068
|
+
boundary_indices
|
|
1069
|
+
volumes
|
|
1070
|
+
edge_mesh/
|
|
1071
|
+
edges
|
|
1072
|
+
edge_centers
|
|
1073
|
+
directions
|
|
1074
|
+
edge_lengths
|
|
1075
|
+
dual_face_areas
|
|
1076
|
+
boundary_edge_indices
|
|
1077
|
+
|
|
1078
|
+
options/
|
|
1079
|
+
attrs:
|
|
1080
|
+
solve_time
|
|
1081
|
+
skip_time
|
|
1082
|
+
dt_init
|
|
1083
|
+
dt_max
|
|
1084
|
+
adaptive
|
|
1085
|
+
save_every
|
|
1086
|
+
include_screening
|
|
1087
|
+
gpu
|
|
1088
|
+
|
|
1089
|
+
data/
|
|
1090
|
+
0/
|
|
1091
|
+
attrs:
|
|
1092
|
+
dt
|
|
1093
|
+
time
|
|
1094
|
+
step
|
|
1095
|
+
psi
|
|
1096
|
+
mu
|
|
1097
|
+
supercurrent
|
|
1098
|
+
normal_current
|
|
1099
|
+
A_applied
|
|
1100
|
+
A_induced
|
|
1101
|
+
1/
|
|
1102
|
+
...
|
|
1103
|
+
|
|
1104
|
+
dynamics/
|
|
1105
|
+
time
|
|
1106
|
+
dt
|
|
1107
|
+
step
|
|
1108
|
+
free_energy
|
|
1109
|
+
mu
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
`psi` is stored as two columns: real and imaginary parts. The `Solution` and
|
|
1113
|
+
`TDGLData` loaders reconstruct it as a complex array.
|
|
1114
|
+
|
|
1115
|
+
Because the device and mesh are embedded in the solution file, most analysis
|
|
1116
|
+
and visualization functions do not require the original Python script that
|
|
1117
|
+
created the simulation.
|
|
1118
|
+
|
|
1119
|
+
## Testing and Validation
|
|
1120
|
+
|
|
1121
|
+
Run the test suite:
|
|
1122
|
+
|
|
1123
|
+
```bash
|
|
1124
|
+
pytest tdgl3d/test
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
Or use the package helper:
|
|
1128
|
+
|
|
1129
|
+
```python
|
|
1130
|
+
import tdgl3d
|
|
1131
|
+
tdgl3d.testing.run()
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
The tests exercise:
|
|
1135
|
+
|
|
1136
|
+
- Geometry generation and closed-volume containment checks.
|
|
1137
|
+
- Tetrahedral mesh generation and finite-volume topology.
|
|
1138
|
+
- Mesh quality metrics.
|
|
1139
|
+
- Gradient, divergence, Laplacian, and link-variable construction.
|
|
1140
|
+
- Short TDGL solves on cone and box geometries.
|
|
1141
|
+
- Seed-solution continuation and interpolation across meshes.
|
|
1142
|
+
- Time-dependent pair-breaking/disorder functions.
|
|
1143
|
+
- Fluxoid contour generation and fluxoid computation.
|
|
1144
|
+
- Numba screening kernels and optional CPU/GPU consistency.
|
|
1145
|
+
- HDF5 device and solution round trips.
|
|
1146
|
+
- Solver option storage.
|
|
1147
|
+
- Terminal current validation.
|
|
1148
|
+
- Solution analysis methods.
|
|
1149
|
+
- Snapshot, animation, monitor, and CLI import paths.
|
|
1150
|
+
- Plotting helpers and visualization utilities.
|
|
1151
|
+
|
|
1152
|
+
For a quick smoke test, run:
|
|
1153
|
+
|
|
1154
|
+
```bash
|
|
1155
|
+
pytest tdgl3d/test/test_fast.py
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
For simulation-level tests, run:
|
|
1159
|
+
|
|
1160
|
+
```bash
|
|
1161
|
+
pytest tdgl3d/test/test_cone.py
|
|
1162
|
+
pytest tdgl3d/test/test_features.py
|
|
1163
|
+
```
|
|
1164
|
+
|
|
1165
|
+
## Reproducibility Checklist
|
|
1166
|
+
|
|
1167
|
+
For reproducible simulation studies, record:
|
|
1168
|
+
|
|
1169
|
+
- Package version and dependency versions:
|
|
1170
|
+
|
|
1171
|
+
```python
|
|
1172
|
+
import tdgl3d
|
|
1173
|
+
print(tdgl3d.version_table(verbose=True))
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
- Python version and operating system.
|
|
1177
|
+
- Geometry generator parameters or the exact custom surface mesh.
|
|
1178
|
+
- Material parameters in `Layer`.
|
|
1179
|
+
- Device length units.
|
|
1180
|
+
- Mesh generation parameters: `max_edge_length`, `min_points`.
|
|
1181
|
+
- Solver options.
|
|
1182
|
+
- Applied vector potential definition.
|
|
1183
|
+
- Disorder or pair-breaking function.
|
|
1184
|
+
- Random-noise amplitude and random seed behavior where relevant.
|
|
1185
|
+
- Whether screening and GPU acceleration were enabled.
|
|
1186
|
+
- Output HDF5 file.
|
|
1187
|
+
|
|
1188
|
+
The HDF5 solution stores most simulation metadata, including the embedded
|
|
1189
|
+
device, mesh, options, saved TDGL snapshots, and dynamics.
|
|
1190
|
+
|
|
1191
|
+
## Troubleshooting
|
|
1192
|
+
|
|
1193
|
+
### Mesh Generation Is Slow
|
|
1194
|
+
|
|
1195
|
+
Reduce `min_points`, increase `max_edge_length`, or start with a simpler
|
|
1196
|
+
surface mesh. Very fine surface meshes and small edge-length targets can create
|
|
1197
|
+
large Delaunay problems.
|
|
1198
|
+
|
|
1199
|
+
### Solver Diverges or Reports NaN Values
|
|
1200
|
+
|
|
1201
|
+
Try:
|
|
1202
|
+
|
|
1203
|
+
- Decrease `dt_init`.
|
|
1204
|
+
- Decrease `dt_max`.
|
|
1205
|
+
- Increase mesh resolution.
|
|
1206
|
+
- Reduce abrupt changes in time-dependent fields.
|
|
1207
|
+
- Use `seed_noise=0.0` for stable Meissner-like initialization.
|
|
1208
|
+
- Use a seed solution for continuation in field/current sweeps.
|
|
1209
|
+
|
|
1210
|
+
### No Data Appears in the Output File
|
|
1211
|
+
|
|
1212
|
+
Check `save_every`, `solve_time`, and `skip_time`. Snapshots are saved when
|
|
1213
|
+
simulation time has passed `skip_time` and the solver step is divisible by
|
|
1214
|
+
`save_every`.
|
|
1215
|
+
|
|
1216
|
+
### GPU Screening Fails
|
|
1217
|
+
|
|
1218
|
+
Check that:
|
|
1219
|
+
|
|
1220
|
+
- CuPy is installed.
|
|
1221
|
+
- The CuPy package matches the CUDA runtime.
|
|
1222
|
+
- A CUDA-capable GPU is visible.
|
|
1223
|
+
- The mesh is small enough for available GPU memory.
|
|
1224
|
+
|
|
1225
|
+
Set `gpu=False` to use the Numba CPU path.
|
|
1226
|
+
|
|
1227
|
+
### Interactive or 3D Visualization Fails
|
|
1228
|
+
|
|
1229
|
+
Install optional visualization dependencies:
|
|
1230
|
+
|
|
1231
|
+
```bash
|
|
1232
|
+
pip install pyvista meshio
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
For headless servers, use Matplotlib's non-GUI backend:
|
|
1236
|
+
|
|
1237
|
+
```python
|
|
1238
|
+
with tdgl3d.non_gui_backend():
|
|
1239
|
+
figures = tdgl3d.generate_snapshots("run.h5")
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
## Known Limitations
|
|
1243
|
+
|
|
1244
|
+
- The mesher is intended for accessible Python workflows and exploratory
|
|
1245
|
+
simulations. It is not a replacement for specialized constrained
|
|
1246
|
+
tetrahedral mesh generators for very complex CAD geometries.
|
|
1247
|
+
- Holes are represented in the `Device` data model, but robust hole-aware
|
|
1248
|
+
constrained meshing requires careful geometry preparation.
|
|
1249
|
+
- Dual-face areas are approximate and based on local tetrahedral geometry.
|
|
1250
|
+
- Screening uses a direct Biot-Savart summation, which can be expensive for
|
|
1251
|
+
large meshes.
|
|
1252
|
+
- Current-through-surface helpers use nearest-edge interpolation and should be
|
|
1253
|
+
interpreted as practical post-processing estimates.
|
|
1254
|
+
- The solver operates in dimensionless TDGL units internally; dimensional
|
|
1255
|
+
interpretation requires consistent material parameters and length units.
|
|
1256
|
+
- Sparse solver options currently validate several backend names, but the
|
|
1257
|
+
implemented solve path primarily uses SciPy sparse direct solvers.
|
|
1258
|
+
|
|
1259
|
+
## License
|
|
1260
|
+
|
|
1261
|
+
MIT License.
|
|
1262
|
+
|
|
1263
|
+
## Citation
|
|
1264
|
+
|
|
1265
|
+
If you use our work, please cite:
|
|
1266
|
+
|
|
1267
|
+
Tanvir Hassan and A. Hasnat, `tdgl3d: Three-Dimensional Time-Dependent
|
|
1268
|
+
Ginzburg-Landau Solver in Python` (manuscript submitted).
|