llg3d 1.4.1__py3-none-any.whl → 2.0.1__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.
- llg3d/__init__.py +6 -1
- llg3d/__main__.py +6 -0
- llg3d/element.py +134 -0
- llg3d/grid.py +123 -0
- llg3d/main.py +67 -0
- llg3d/output.py +107 -0
- llg3d/parameters.py +75 -0
- llg3d/post/__init__.py +1 -1
- llg3d/post/plot_results.py +61 -0
- llg3d/post/process.py +18 -13
- llg3d/post/temperature.py +6 -14
- llg3d/simulation.py +95 -0
- llg3d/solver/__init__.py +45 -0
- llg3d/solver/jax.py +387 -0
- llg3d/solver/mpi.py +450 -0
- llg3d/solver/numpy.py +207 -0
- llg3d/solver/opencl.py +330 -0
- llg3d/solver/solver.py +89 -0
- {llg3d-1.4.1.dist-info → llg3d-2.0.1.dist-info}/METADATA +14 -22
- llg3d-2.0.1.dist-info/RECORD +25 -0
- {llg3d-1.4.1.dist-info → llg3d-2.0.1.dist-info}/WHEEL +1 -1
- llg3d-2.0.1.dist-info/entry_points.txt +4 -0
- llg3d/llg3d.py +0 -742
- llg3d/llg3d_seq.py +0 -447
- llg3d-1.4.1.dist-info/RECORD +0 -13
- llg3d-1.4.1.dist-info/entry_points.txt +0 -3
- {llg3d-1.4.1.dist-info → llg3d-2.0.1.dist-info/licenses}/AUTHORS +0 -0
- {llg3d-1.4.1.dist-info → llg3d-2.0.1.dist-info/licenses}/LICENSE +0 -0
- {llg3d-1.4.1.dist-info → llg3d-2.0.1.dist-info}/top_level.txt +0 -0
llg3d/solver/opencl.py
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"""LLG3D Solver using OpenCL."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pyopencl as cl
|
|
8
|
+
from pyopencl import clrandom
|
|
9
|
+
from pyopencl import array as clarray
|
|
10
|
+
|
|
11
|
+
from ..output import progress_bar, get_output_files, close_output_files
|
|
12
|
+
from ..grid import Grid
|
|
13
|
+
from ..element import Element, Cobalt
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_context_and_device(
|
|
17
|
+
device_selection: str = "auto",
|
|
18
|
+
) -> tuple[cl.Context, cl.Device]:
|
|
19
|
+
"""
|
|
20
|
+
Get the OpenCL context and device.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
device_selection:
|
|
24
|
+
|
|
25
|
+
- ``"auto"``: Let OpenCL choose automatically
|
|
26
|
+
- ``"cpu"``: Select CPU device
|
|
27
|
+
- ``"gpu"``: Select first available GPU
|
|
28
|
+
- ``"gpu:N"``: Select specific GPU by index (e.g., ``"gpu:0"``, ``"gpu:1"``)
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
- The OpenCL context
|
|
32
|
+
- The OpenCL device
|
|
33
|
+
"""
|
|
34
|
+
if device_selection == "auto":
|
|
35
|
+
context = cl.create_some_context(interactive=False)
|
|
36
|
+
device = context.devices[0]
|
|
37
|
+
return context, device
|
|
38
|
+
|
|
39
|
+
# Get all platforms and devices
|
|
40
|
+
platforms = cl.get_platforms()
|
|
41
|
+
all_devices = []
|
|
42
|
+
|
|
43
|
+
for platform in platforms:
|
|
44
|
+
all_devices.extend(platform.get_devices())
|
|
45
|
+
|
|
46
|
+
if not all_devices:
|
|
47
|
+
raise RuntimeError("No OpenCL devices found")
|
|
48
|
+
|
|
49
|
+
# Filter devices based on selection
|
|
50
|
+
if device_selection == "cpu":
|
|
51
|
+
cpu_devices = [d for d in all_devices if d.type & cl.device_type.CPU]
|
|
52
|
+
if not cpu_devices:
|
|
53
|
+
raise RuntimeError("No CPU devices found")
|
|
54
|
+
selected_device = cpu_devices[0]
|
|
55
|
+
elif device_selection == "gpu":
|
|
56
|
+
gpu_devices = [d for d in all_devices if d.type & cl.device_type.GPU]
|
|
57
|
+
if not gpu_devices:
|
|
58
|
+
raise RuntimeError("No GPU devices found")
|
|
59
|
+
selected_device = gpu_devices[0]
|
|
60
|
+
elif device_selection.startswith("gpu:"):
|
|
61
|
+
gpu_devices = [d for d in all_devices if d.type & cl.device_type.GPU]
|
|
62
|
+
if not gpu_devices:
|
|
63
|
+
raise RuntimeError("No GPU devices found")
|
|
64
|
+
|
|
65
|
+
gpu_index = int(device_selection.split(":")[1])
|
|
66
|
+
if gpu_index >= len(gpu_devices):
|
|
67
|
+
raise RuntimeError(
|
|
68
|
+
f"GPU index {gpu_index} not available. Found {len(gpu_devices)} GPU(s)"
|
|
69
|
+
)
|
|
70
|
+
selected_device = gpu_devices[gpu_index]
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(f"Invalid device selection: {device_selection}")
|
|
73
|
+
|
|
74
|
+
# Create context with selected device
|
|
75
|
+
context = cl.Context([selected_device])
|
|
76
|
+
print(f"Selected OpenCL device: {selected_device.name} ({selected_device.type})")
|
|
77
|
+
|
|
78
|
+
return context, selected_device
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_precision(device: cl.Device, precision: str) -> np.dtype:
|
|
82
|
+
"""
|
|
83
|
+
Get the numpy float type based on the precision.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
device: OpenCL device
|
|
87
|
+
precision: Precision of the simulation (single or double)
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The numpy float type (float32 or float64)
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
RuntimeError: If double precision is asked while the device does not support it
|
|
94
|
+
"""
|
|
95
|
+
# Check that cl device supports double precision
|
|
96
|
+
if precision == "double" and not device.double_fp_config:
|
|
97
|
+
raise RuntimeError("The selected device does not support double precision.")
|
|
98
|
+
|
|
99
|
+
return np.float64 if precision == "double" else np.float32
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class Program:
|
|
103
|
+
"""Class to manage the OpenCL kernels for the LLG3D simulation."""
|
|
104
|
+
|
|
105
|
+
def __init__(self, g: Grid, context: cl.Context, np_float: np.dtype):
|
|
106
|
+
self.grid = g
|
|
107
|
+
self.context = context
|
|
108
|
+
self.np_float = np_float
|
|
109
|
+
self.cl_program = self._get_built_program()
|
|
110
|
+
|
|
111
|
+
def _get_built_program(self) -> cl.Program:
|
|
112
|
+
"""
|
|
113
|
+
Return the OpenCL program built from the source code.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
The OpenCL program object
|
|
117
|
+
"""
|
|
118
|
+
opencl_code = (Path(__file__).parent / "llg3d.cl").read_text()
|
|
119
|
+
build_options = "-D USE_DOUBLE_PRECISION" if self.np_float == np.float64 else ""
|
|
120
|
+
build_options += (
|
|
121
|
+
f" -D NX={self.grid.Jx} -D NY={self.grid.Jy} -D NZ={self.grid.Jz}"
|
|
122
|
+
)
|
|
123
|
+
return cl.Program(self.context, opencl_code).build(options=build_options)
|
|
124
|
+
|
|
125
|
+
def get_kernel(self, kernel_name: str, arg_types: list = [None]) -> cl.Kernel:
|
|
126
|
+
"""
|
|
127
|
+
Returns the specified kernel by name.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
kernel_name: Name of the kernel to retrieve
|
|
131
|
+
arg_types: List of argument types for the kernel
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
The OpenCL kernel object
|
|
135
|
+
"""
|
|
136
|
+
kernel: cl.Kernel = getattr(self.cl_program, kernel_name)
|
|
137
|
+
kernel.set_arg_types(arg_types)
|
|
138
|
+
return kernel
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def simulate(
|
|
142
|
+
N: int,
|
|
143
|
+
Jx: int,
|
|
144
|
+
Jy: int,
|
|
145
|
+
Jz: int,
|
|
146
|
+
dx: float,
|
|
147
|
+
T: float,
|
|
148
|
+
H_ext: float,
|
|
149
|
+
dt: float,
|
|
150
|
+
start_averaging: int,
|
|
151
|
+
n_mean: int,
|
|
152
|
+
n_profile: int,
|
|
153
|
+
element_class: Element,
|
|
154
|
+
precision: str,
|
|
155
|
+
seed: int,
|
|
156
|
+
device: str = "auto",
|
|
157
|
+
**_,
|
|
158
|
+
) -> tuple[float, str, float]:
|
|
159
|
+
"""
|
|
160
|
+
Simulates the system over N iterations.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
N: Number of iterations
|
|
164
|
+
Jx: Number of grid points in x direction
|
|
165
|
+
Jy: Number of grid points in y direction
|
|
166
|
+
Jz: Number of grid points in z direction
|
|
167
|
+
dx: Grid spacing
|
|
168
|
+
T: Temperature in Kelvin
|
|
169
|
+
H_ext: External magnetic field strength
|
|
170
|
+
dt: Time step for the simulation
|
|
171
|
+
start_averaging: Number of iterations for averaging
|
|
172
|
+
n_mean: Number of iterations for integral output
|
|
173
|
+
n_profile: Number of iterations for profile output
|
|
174
|
+
element_class: Element of the sample (default: Cobalt)
|
|
175
|
+
precision: Precision of the simulation (single or double)
|
|
176
|
+
seed: Random seed for temperature fluctuations
|
|
177
|
+
device: Device to use ('cpu', 'gpu', 'gpu:0', 'gpu:1', etc., or 'auto')
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
- The grid object
|
|
181
|
+
- The time taken for the simulation
|
|
182
|
+
- The output filename
|
|
183
|
+
"""
|
|
184
|
+
context, opencl_device = get_context_and_device(device)
|
|
185
|
+
np_float = get_precision(opencl_device, precision)
|
|
186
|
+
|
|
187
|
+
g = Grid(Jx, Jy, Jz, dx)
|
|
188
|
+
print(g)
|
|
189
|
+
|
|
190
|
+
e = element_class(T, H_ext, g, dt)
|
|
191
|
+
if not isinstance(e, Cobalt):
|
|
192
|
+
raise NotImplementedError(
|
|
193
|
+
f"Element is {type(e)} but only {Cobalt} is supported at the moment."
|
|
194
|
+
)
|
|
195
|
+
print(f"CFL = {e.get_CFL()}")
|
|
196
|
+
|
|
197
|
+
# --- Initialization ---
|
|
198
|
+
|
|
199
|
+
def theta_init(shape):
|
|
200
|
+
"""Initialization of theta."""
|
|
201
|
+
return np.zeros(shape, dtype=np_float)
|
|
202
|
+
|
|
203
|
+
def phi_init(t, shape):
|
|
204
|
+
"""Initialization of phi."""
|
|
205
|
+
return np.zeros(shape, dtype=np_float) + e.gamma_0 * H_ext * t
|
|
206
|
+
|
|
207
|
+
m_n = np.zeros((3,) + g.dims, dtype=np_float)
|
|
208
|
+
|
|
209
|
+
theta = theta_init(g.dims)
|
|
210
|
+
phi = phi_init(0, g.dims)
|
|
211
|
+
|
|
212
|
+
m_n[0] = np.cos(theta)
|
|
213
|
+
m_n[1] = np.sin(theta) * np.cos(phi)
|
|
214
|
+
m_n[2] = np.sin(theta) * np.sin(phi)
|
|
215
|
+
|
|
216
|
+
queue = cl.CommandQueue(context)
|
|
217
|
+
|
|
218
|
+
program = Program(g, context, np_float)
|
|
219
|
+
slope_kernel = program.get_kernel("slope", [None] * 3 + [np_float] * 8)
|
|
220
|
+
update_1_kernel = program.get_kernel("update_1", [None] * 3 + [np_float])
|
|
221
|
+
update_2_kernel = program.get_kernel("update_2", [None] * 4 + [np_float])
|
|
222
|
+
normalize_kernel = program.get_kernel("normalize")
|
|
223
|
+
|
|
224
|
+
# Create a CL array for m1 component in order to compute averages
|
|
225
|
+
d_m1 = clarray.empty(queue, g.ntot, np_float)
|
|
226
|
+
copy_m1_kernel = program.get_kernel("copy_m1", [None, None])
|
|
227
|
+
|
|
228
|
+
mf = cl.mem_flags
|
|
229
|
+
mem_size = m_n.nbytes
|
|
230
|
+
|
|
231
|
+
d_m_n = cl.Buffer(context, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=m_n)
|
|
232
|
+
d_R_alea = cl.array.Array(queue, (3,) + g.dims, np_float)
|
|
233
|
+
d_m_np1 = cl.Buffer(context, mf.READ_WRITE, mem_size)
|
|
234
|
+
d_s_pre = cl.Buffer(context, mf.READ_WRITE, mem_size)
|
|
235
|
+
d_s_cor = cl.Buffer(context, mf.READ_WRITE, mem_size)
|
|
236
|
+
|
|
237
|
+
rng = clrandom.PhiloxGenerator(context, seed=seed)
|
|
238
|
+
|
|
239
|
+
f_mean, f_profiles, output_filenames = get_output_files(g, T, n_mean, n_profile)
|
|
240
|
+
|
|
241
|
+
t = 0.0
|
|
242
|
+
m1_average = 0.0
|
|
243
|
+
|
|
244
|
+
start_time = time.perf_counter()
|
|
245
|
+
|
|
246
|
+
for n in progress_bar(range(1, N + 1), "Iteration : ", 40):
|
|
247
|
+
t += dt
|
|
248
|
+
|
|
249
|
+
rng.fill_normal(d_R_alea)
|
|
250
|
+
queue.finish() # ensure the array is filled
|
|
251
|
+
|
|
252
|
+
# Prediction phase
|
|
253
|
+
|
|
254
|
+
# calculate s_i_pre from m_i^n
|
|
255
|
+
slope_kernel(
|
|
256
|
+
queue,
|
|
257
|
+
g.dims,
|
|
258
|
+
None,
|
|
259
|
+
d_m_n,
|
|
260
|
+
d_R_alea.data,
|
|
261
|
+
d_s_pre,
|
|
262
|
+
g.dx,
|
|
263
|
+
g.dy,
|
|
264
|
+
g.dz,
|
|
265
|
+
e.coeff_1,
|
|
266
|
+
e.coeff_2,
|
|
267
|
+
e.coeff_3,
|
|
268
|
+
e.coeff_4,
|
|
269
|
+
e.lambda_G,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# m_i^n+1 = m_i^n + dt * s_i_pre
|
|
273
|
+
update_1_kernel(queue, (3 * g.ntot,), None, d_m_n, d_m_np1, d_s_pre, dt)
|
|
274
|
+
queue.finish()
|
|
275
|
+
|
|
276
|
+
# # Correction phase
|
|
277
|
+
|
|
278
|
+
# calculate s_i_cor from m_i^n+1
|
|
279
|
+
slope_kernel(
|
|
280
|
+
queue,
|
|
281
|
+
g.dims,
|
|
282
|
+
None,
|
|
283
|
+
d_m_np1,
|
|
284
|
+
d_R_alea.data,
|
|
285
|
+
d_s_cor,
|
|
286
|
+
g.dx,
|
|
287
|
+
g.dy,
|
|
288
|
+
g.dz,
|
|
289
|
+
e.coeff_1,
|
|
290
|
+
e.coeff_2,
|
|
291
|
+
e.coeff_3,
|
|
292
|
+
e.coeff_4,
|
|
293
|
+
e.lambda_G,
|
|
294
|
+
)
|
|
295
|
+
# m_i^n+1 = m_i^n + dt * (s_i_pre + s_i_cor) / 2
|
|
296
|
+
# Update using the corrected values
|
|
297
|
+
update_2_kernel(
|
|
298
|
+
queue, (3 * g.ntot,), None, d_m_n, d_m_np1, d_s_pre, d_s_cor, dt
|
|
299
|
+
)
|
|
300
|
+
queue.finish()
|
|
301
|
+
|
|
302
|
+
# Normalization
|
|
303
|
+
normalize_kernel(queue, (g.ntot,), None, d_m_np1).wait()
|
|
304
|
+
|
|
305
|
+
# Swap the buffers for the next iteration
|
|
306
|
+
d_m_n, d_m_np1 = d_m_np1, d_m_n
|
|
307
|
+
|
|
308
|
+
# Space average of m_1 using the midpoint method with OpenCL sum
|
|
309
|
+
if n_mean != 0 and n % n_mean == 0:
|
|
310
|
+
# Copy only the first component from d_m_n to d_m1 with weights applied
|
|
311
|
+
# d_m_n contains [m1, m2, m3] interleaved, we want only m1
|
|
312
|
+
copy_m1_kernel(queue, g.dims, None, d_m_n, d_m1.data)
|
|
313
|
+
|
|
314
|
+
# Use PyOpenCL array sum to compute the weighted sum
|
|
315
|
+
weighted_sum = clarray.sum(d_m1).get()
|
|
316
|
+
m1_mean = weighted_sum / g.ncell
|
|
317
|
+
|
|
318
|
+
if n >= start_averaging:
|
|
319
|
+
m1_average += m1_mean * n_mean
|
|
320
|
+
|
|
321
|
+
f_mean.write(f"{t:10.8e} {m1_mean:10.8e}\n")
|
|
322
|
+
|
|
323
|
+
total_time = time.perf_counter() - start_time
|
|
324
|
+
|
|
325
|
+
close_output_files(f_mean, f_profiles)
|
|
326
|
+
|
|
327
|
+
if n > start_averaging:
|
|
328
|
+
m1_average /= N - start_averaging
|
|
329
|
+
|
|
330
|
+
return total_time, output_filenames, m1_average
|
llg3d/solver/solver.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Common functions for the LLG3D solver."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from ..grid import Grid
|
|
6
|
+
from ..element import Element
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def cross_product(a: np.ndarray, b: np.ndarray) -> np.ndarray:
|
|
10
|
+
r"""
|
|
11
|
+
Compute cross product :math:`a \times b`.
|
|
12
|
+
|
|
13
|
+
This implementation is faster than np.cross for large arrays.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
a: First vector (shape (3, nx, ny, nz))
|
|
17
|
+
b: Second vector (shape (3, nx, ny, nz))
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Cross product :math:`a \times b` (shape (3, nx, ny, nz))
|
|
21
|
+
"""
|
|
22
|
+
return np.stack(
|
|
23
|
+
[
|
|
24
|
+
a[1] * b[2] - a[2] * b[1], # x-component
|
|
25
|
+
a[2] * b[0] - a[0] * b[2], # y-component
|
|
26
|
+
a[0] * b[1] - a[1] * b[0], # z-component
|
|
27
|
+
],
|
|
28
|
+
axis=0,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def compute_H_anisotropy(e: Element, m: np.ndarray) -> np.ndarray:
|
|
33
|
+
"""
|
|
34
|
+
Compute the anisotropy field.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
e: Element object
|
|
38
|
+
m: Magnetization array (shape (3, nx, ny, nz)).
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Anisotropy field array (shape (3, nx, ny, nz))
|
|
42
|
+
"""
|
|
43
|
+
m1, m2, m3 = m
|
|
44
|
+
|
|
45
|
+
m1m1 = m1 * m1
|
|
46
|
+
m2m2 = m2 * m2
|
|
47
|
+
m3m3 = m3 * m3
|
|
48
|
+
|
|
49
|
+
if e.anisotropy == "uniaxial":
|
|
50
|
+
aniso_1 = m1
|
|
51
|
+
aniso_2 = np.zeros_like(m1)
|
|
52
|
+
aniso_3 = np.zeros_like(m1)
|
|
53
|
+
|
|
54
|
+
if e.anisotropy == "cubic":
|
|
55
|
+
aniso_1 = -(1 - m1m1 + m2m2 * m3m3) * m1
|
|
56
|
+
aniso_2 = -(1 - m2m2 + m1m1 * m3m3) * m2
|
|
57
|
+
aniso_3 = -(1 - m3m3 + m1m1 * m2m2) * m3
|
|
58
|
+
|
|
59
|
+
return e.coeff_2 * np.stack([aniso_1, aniso_2, aniso_3], axis=0)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def space_average(g: Grid, m: np.ndarray, copy: bool = True) -> float:
|
|
63
|
+
"""
|
|
64
|
+
Returns the spatial average of m with shape (g.dims) using the midpoint method.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
g: Grid object
|
|
68
|
+
m: Array to be integrated
|
|
69
|
+
copy: If True, copy m to avoid modifying its value
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Spatial average of m
|
|
73
|
+
"""
|
|
74
|
+
# copy m to avoid modifying its value
|
|
75
|
+
mm = m.copy() if copy else m
|
|
76
|
+
|
|
77
|
+
# on the edges, we divide the contribution by 2
|
|
78
|
+
# x
|
|
79
|
+
mm[0, :, :] /= 2
|
|
80
|
+
mm[-1, :, :] /= 2
|
|
81
|
+
# y
|
|
82
|
+
mm[:, 0, :] /= 2
|
|
83
|
+
mm[:, -1, :] /= 2
|
|
84
|
+
# z
|
|
85
|
+
mm[:, :, 0] /= 2
|
|
86
|
+
mm[:, :, -1] /= 2
|
|
87
|
+
|
|
88
|
+
average = mm.sum() / g.ncell
|
|
89
|
+
return average
|
|
@@ -1,36 +1,28 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: llg3d
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: A solver for the stochastic Landau-Lifshitz-Gilbert equation in 3D
|
|
5
5
|
Author-email: Clémentine Courtès <clementine.courtes@math.unistra.fr>, Matthieu Boileau <matthieu.boileau@math.unistra.fr>
|
|
6
6
|
Project-URL: Homepage, https://gitlab.math.unistra.fr/llg3d/llg3d
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
License-File: AUTHORS
|
|
14
14
|
Requires-Dist: numpy
|
|
15
|
-
Requires-Dist: mpi4py
|
|
16
15
|
Requires-Dist: matplotlib
|
|
17
16
|
Requires-Dist: scipy
|
|
18
|
-
Provides-Extra:
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
|
|
27
|
-
Requires-Dist: sphinxcontrib-programoutput ; extra == 'doc'
|
|
28
|
-
Requires-Dist: sphinxcontrib-bibtex ; extra == 'doc'
|
|
29
|
-
Requires-Dist: ipython ; extra == 'doc'
|
|
30
|
-
Provides-Extra: test
|
|
31
|
-
Requires-Dist: pytest ; extra == 'test'
|
|
32
|
-
Requires-Dist: pytest-cov ; extra == 'test'
|
|
33
|
-
Requires-Dist: pytest-mpi ; extra == 'test'
|
|
17
|
+
Provides-Extra: mpi
|
|
18
|
+
Requires-Dist: mpi4py; extra == "mpi"
|
|
19
|
+
Provides-Extra: opencl
|
|
20
|
+
Requires-Dist: pyopencl; extra == "opencl"
|
|
21
|
+
Requires-Dist: mako; extra == "opencl"
|
|
22
|
+
Provides-Extra: jax
|
|
23
|
+
Requires-Dist: jax[cuda]; sys_platform != "darwin" and extra == "jax"
|
|
24
|
+
Requires-Dist: jax[cpu]; sys_platform == "darwin" and extra == "jax"
|
|
25
|
+
Dynamic: license-file
|
|
34
26
|
|
|
35
27
|
# LLG3D: A solver for the stochastic Landau-Lifshitz-Gilbert equation in 3D
|
|
36
28
|
|
|
@@ -40,6 +32,6 @@ Requires-Dist: pytest-mpi ; extra == 'test'
|
|
|
40
32
|
[](https://llg3d.pages.math.unistra.fr/llg3d/)
|
|
41
33
|
[](https://archive.softwareheritage.org/browse/origin/?origin_url=https://gitlab.math.unistra.fr/llg3d/llg3d)
|
|
42
34
|
|
|
43
|
-
LLG3D is written in Python and
|
|
35
|
+
LLG3D is written in Python and may run in parallel using MPI, OpenCL or JAX.
|
|
44
36
|
|
|
45
37
|
See the [documentation](https://llg3d.pages.math.unistra.fr/llg3d/).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
llg3d/__init__.py,sha256=YhFE4dB9NPWdcadzg14xMgOao1PqDgzxU2iFKEp7Vjs,187
|
|
2
|
+
llg3d/__main__.py,sha256=3D1q7AG5vU6gr-V0iuo5oNYl-Og2SvJ4YaBTdqOVVaw,115
|
|
3
|
+
llg3d/element.py,sha256=B2GDv5lTNTgvbIqSThEaA2o3hQRkhQdIgXg-MxzeSuc,4304
|
|
4
|
+
llg3d/grid.py,sha256=goXO-sh9wJ2YDqe3IerqE6NlvUFacp328RfXHHW6bFY,3848
|
|
5
|
+
llg3d/main.py,sha256=8ltKUT9jA3u1E0cEUjxemRfRPhERnIm_i5L6sP0_G0I,1888
|
|
6
|
+
llg3d/output.py,sha256=HCL1V1oehbV2OERwbS0WCG00W1kNeNQpgcP33LIrGWU,2934
|
|
7
|
+
llg3d/parameters.py,sha256=VoAkm8v6MiOkwRaHupWgHVTQaSDSGIKz5jWnRT8PBx0,2442
|
|
8
|
+
llg3d/simulation.py,sha256=8RbAzLhlhCEZZCGxqNEczxGvfrLsnxKbRCkfpy1cCtU,3429
|
|
9
|
+
llg3d/post/__init__.py,sha256=rX0jQIXGQYzFRoXSsPu2oEDB4YWK5IxFs0cJqXnQb8g,39
|
|
10
|
+
llg3d/post/plot_results.py,sha256=fmCxeKmRfpHa_sNHMqczYNWQOCLJzxfRDQL22AeeJcM,1550
|
|
11
|
+
llg3d/post/process.py,sha256=YKOUIgjAOD3q6_qoxzWVSC0WHBofRZJafwWzdMXWQXA,3569
|
|
12
|
+
llg3d/post/temperature.py,sha256=SHf3s1LHIY0SBDKLoSMGSKo1e7EXcKeMXZdlEsChCBk,2150
|
|
13
|
+
llg3d/solver/__init__.py,sha256=fqd1wZkIAi0sMiOQg0tbfLInMDjTCFkRF5NA_FV1wyU,901
|
|
14
|
+
llg3d/solver/jax.py,sha256=VKn7YanidgXhpoIKEJy0sL0HZ1wV3a9F1uWmbAmWOi0,12026
|
|
15
|
+
llg3d/solver/mpi.py,sha256=hJSXbZxpmi3z6PyMxyvuSBHFaqDv4S_C2FnAwAgI_r4,13041
|
|
16
|
+
llg3d/solver/numpy.py,sha256=3trvsn2W-CjGdFF70oQZ6Jw8avX47OJ1DighschMcHE,6040
|
|
17
|
+
llg3d/solver/opencl.py,sha256=EEExOmSUDs0UPt-E4XvPv4NA_S4SuIqw5Uo9p4KghcM,10222
|
|
18
|
+
llg3d/solver/solver.py,sha256=MXscOjO0-RUIqOir1D23_8xCFw8ulDADTFQwQbMkBvc,2142
|
|
19
|
+
llg3d-2.0.1.dist-info/licenses/AUTHORS,sha256=vhJ88HikYvOrGiB_l1xH2X6hyn9ZJafx6mpoMYNhh1I,297
|
|
20
|
+
llg3d-2.0.1.dist-info/licenses/LICENSE,sha256=aFxTGAyyve8nM9T2jWTarJzQhdSSC3MbbN1heNAev9c,1062
|
|
21
|
+
llg3d-2.0.1.dist-info/METADATA,sha256=J7kKUK6YL7cIKhHmEJLMud6rzr93AgCGozbmHIYVwPY,1948
|
|
22
|
+
llg3d-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
llg3d-2.0.1.dist-info/entry_points.txt,sha256=kjuf09uDEuROnDeuHkVen3oAS4ThEPS084TpHmQAbJ8,133
|
|
24
|
+
llg3d-2.0.1.dist-info/top_level.txt,sha256=cBZ0roaXt3CAXqYojuO84lGPCtWuLlXxLGLYRKmHZy0,6
|
|
25
|
+
llg3d-2.0.1.dist-info/RECORD,,
|