llg3d 2.0.0__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 CHANGED
@@ -1,4 +1,6 @@
1
+ """Main llg3d package."""
2
+
1
3
  from .solver import LIB_AVAILABLE, rank, size, comm, status
2
4
 
3
- __version__ = "2.0.0"
5
+ __version__ = "2.0.1"
4
6
  __all__ = ["LIB_AVAILABLE", "rank", "size", "comm", "status", "__version__"]
llg3d/__main__.py CHANGED
@@ -1,6 +1,6 @@
1
- # Enable to use llg3d as a module
1
+ """Main entry point to execute the llg3d module."""
2
2
 
3
3
  from .main import main
4
4
 
5
5
  if __name__ == "__main__":
6
- main()
6
+ main()
llg3d/element.py CHANGED
@@ -1,6 +1,6 @@
1
- r"""
2
- Module containing the definition of the chemical elements
3
- """
1
+ """Module containing the definition of the chemical elements."""
2
+
3
+ from abc import ABC
4
4
 
5
5
  import numpy as np
6
6
 
@@ -11,8 +11,16 @@ mu_0 = 4 * np.pi * 1.0e-7 #: Vacuum permeability :math:`[H.m^{-1}]`
11
11
  gamma = 1.76e11 #: Gyromagnetic ratio :math:`[rad.s^{-1}.T^{-1}]`
12
12
 
13
13
 
14
- class Element:
15
- """Abstract class for chemical elements"""
14
+ class Element(ABC):
15
+ """
16
+ Abstract class for chemical elements.
17
+
18
+ Args:
19
+ T: Temperature in Kelvin
20
+ H_ext: External magnetic field strength
21
+ g: Grid object representing the simulation grid
22
+ dt: Time step for the simulation
23
+ """
16
24
 
17
25
  A = 0.0
18
26
  K = 0.0
@@ -22,14 +30,6 @@ class Element:
22
30
  anisotropy: str = ""
23
31
 
24
32
  def __init__(self, T: float, H_ext: float, g: Grid, dt: float) -> None:
25
- """Initializes the Element class with given parameters
26
-
27
- Args:
28
- T: Temperature in Kelvin
29
- H_ext: External magnetic field strength
30
- g: Grid object representing the simulation grid
31
- dt: Time step for the simulation
32
- """
33
33
  self.H_ext = H_ext
34
34
  self.g = g
35
35
  self.dt = dt
@@ -52,16 +52,16 @@ class Element:
52
52
 
53
53
  def get_CFL(self) -> float:
54
54
  """
55
- Returns the value of the CFL
55
+ Returns the value of the CFL.
56
56
 
57
57
  Returns:
58
- float: The CFL value
58
+ The CFL value
59
59
  """
60
60
  return self.dt * self.coeff_1 / self.g.dx**2
61
61
 
62
62
  def to_dict(self) -> dict:
63
63
  """
64
- Export element parameters to a dictionary for JAX JIT compatibility
64
+ Export element parameters to a dictionary for JAX JIT compatibility.
65
65
 
66
66
  Returns:
67
67
  Dictionary containing element parameters needed for computations
@@ -81,6 +81,8 @@ class Element:
81
81
 
82
82
 
83
83
  class Cobalt(Element):
84
+ """Cobalt element."""
85
+
84
86
  A = 30.0e-12 #: Exchange constant :math:`[J.m^{-1}]`
85
87
  K = 520.0e3 #: Anisotropy constant :math:`[J.m^{-3}]`
86
88
  lambda_G = 0.5 #: Damping parameter :math:`[1]`
@@ -90,6 +92,8 @@ class Cobalt(Element):
90
92
 
91
93
 
92
94
  class Iron(Element):
95
+ """Iron element."""
96
+
93
97
  A = 21.0e-12 #: Exchange constant :math:`[J.m^{-1}]`
94
98
  K = 48.0e3 #: Anisotropy constant :math:`[J.m^{-3}]`
95
99
  lambda_G = 0.5 #: Damping parameter :math:`[1]`
@@ -99,6 +103,8 @@ class Iron(Element):
99
103
 
100
104
 
101
105
  class Nickel(Element):
106
+ """Nickel element."""
107
+
102
108
  A = 9.0e-12 #: Exchange constant :math:`[J.m^{-1}]`
103
109
  K = -5.7e3 #: Anisotropy constant :math:`[J.m^{-3}]`
104
110
  lambda_G = 0.5 #: Damping parameter :math:`[1]`
llg3d/grid.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- Module to define the grid for the simulation
3
- """
1
+ """Module to define the grid for the simulation."""
4
2
 
5
3
  from dataclasses import dataclass
6
4
  import numpy as np
@@ -10,7 +8,7 @@ from .solver import rank, size
10
8
 
11
9
  @dataclass
12
10
  class Grid:
13
- """Stores grid data"""
11
+ """Stores grid data."""
14
12
 
15
13
  # Parameter values correspond to the global grid
16
14
  Jx: int #: number of points in x direction
@@ -19,7 +17,7 @@ class Grid:
19
17
  dx: float #: grid spacing in x direction
20
18
 
21
19
  def __post_init__(self) -> None:
22
- """Compute grid characteristics"""
20
+ """Compute grid characteristics."""
23
21
  self.dy = self.dz = self.dx # Setting dx = dy = dz
24
22
  self.Lx = (self.Jx - 1) * self.dx
25
23
  self.Ly = (self.Jy - 1) * self.dy
@@ -35,8 +33,7 @@ class Grid:
35
33
  self.ncell = (self.Jx - 1) * (self.Jy - 1) * (self.Jz - 1)
36
34
 
37
35
  def __str__(self) -> str:
38
- """Print grid information"""
39
-
36
+ """Print grid information."""
40
37
  header = "\t\t".join(("x", "y", "z"))
41
38
  s = f"""\
42
39
  \t{header}
@@ -55,7 +52,7 @@ ncell = {self.ncell:d}
55
52
  self, T: float, name: str = "m1_mean", extension: str = "txt"
56
53
  ) -> str:
57
54
  """
58
- Returns the output file name for a given temperature
55
+ Returns the output file name for a given temperature.
59
56
 
60
57
  Args:
61
58
  T: temperature
@@ -97,7 +94,7 @@ ncell = {self.ncell:d}
97
94
 
98
95
  def to_dict(self) -> dict:
99
96
  """
100
- Export grid parameters to a dictionary for JAX JIT compatibility
97
+ Export grid parameters to a dictionary for JAX JIT compatibility.
101
98
 
102
99
  Returns:
103
100
  Dictionary containing grid parameters needed for computations
@@ -114,7 +111,7 @@ ncell = {self.ncell:d}
114
111
 
115
112
  def get_laplacian_coeff(self) -> tuple[float, float, float, float]:
116
113
  """
117
- Returns the coefficients for the laplacian computation
114
+ Returns the coefficients for the laplacian computation.
118
115
 
119
116
  Returns:
120
117
  Tuple of coefficients (dx2_inv, dy2_inv, dz2_inv, center_coeff)
llg3d/main.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- Define a CLI for the llg3d package.
3
- """
1
+ """Define a CLI for the llg3d package."""
4
2
 
5
3
  import argparse
6
4
 
@@ -20,6 +18,7 @@ else:
20
18
  def parse_args(args: list[str] | None) -> argparse.Namespace:
21
19
  """
22
20
  Argument parser for llg3d.
21
+
23
22
  Automatically adds arguments from the parameter dictionary.
24
23
 
25
24
  Returns:
@@ -44,9 +43,11 @@ def parse_args(args: list[str] | None) -> argparse.Namespace:
44
43
 
45
44
  def main(arg_list: list[str] = None):
46
45
  """
47
- Evaluates the command line and runs the simulation
48
- """
46
+ Evaluates the command line and runs the simulation.
49
47
 
48
+ Args:
49
+ arg_list: List of command line arguments
50
+ """
50
51
  args = parse_args(arg_list)
51
52
 
52
53
  if size > 1 and args.solver != "mpi":
llg3d/output.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- Utility functions for LLG3D
3
- """
1
+ """Utility functions for LLG3D."""
4
2
 
5
3
  import json
6
4
  import sys
@@ -10,9 +8,11 @@ from .solver import rank
10
8
  from .grid import Grid
11
9
 
12
10
 
13
- def progress_bar(it: Iterable, prefix: str = "", size: int = 60, out: TextIO = sys.stdout):
11
+ def progress_bar(
12
+ it: Iterable, prefix: str = "", size: int = 60, out: TextIO = sys.stdout
13
+ ):
14
14
  """
15
- Displays a progress bar
15
+ Displays a progress bar.
16
16
 
17
17
  (Source: https://stackoverflow.com/a/34482761/16593179)
18
18
 
@@ -22,14 +22,13 @@ def progress_bar(it: Iterable, prefix: str = "", size: int = 60, out: TextIO = s
22
22
  size: Size of the progress bar (number of characters)
23
23
  out: Output stream (default is sys.stdout)
24
24
  """
25
-
26
25
  count = len(it)
27
26
 
28
27
  def show(j):
29
28
  x = int(size * j / count)
30
29
  if rank == 0:
31
30
  print(
32
- f"{prefix}[{u'█'*x}{('.'*(size-x))}] {j}/{count}",
31
+ f"{prefix}[{'█' * x}{('.' * (size - x))}] {j}/{count}",
33
32
  end="\r",
34
33
  file=out,
35
34
  flush=True,
@@ -48,7 +47,7 @@ def progress_bar(it: Iterable, prefix: str = "", size: int = 60, out: TextIO = s
48
47
 
49
48
  def write_json(json_file: str, run: dict):
50
49
  """
51
- Writes the run dictionary to a JSON file
50
+ Writes the run dictionary to a JSON file.
52
51
 
53
52
  Args:
54
53
  json_file: Name of the JSON file
@@ -60,7 +59,7 @@ def write_json(json_file: str, run: dict):
60
59
 
61
60
  def get_output_files(g: Grid, T: float, n_mean: int, n_profile: int) -> tuple:
62
61
  """
63
- Open files and list them
62
+ Open files and list them.
64
63
 
65
64
  Args:
66
65
  g: Grid object
@@ -95,7 +94,7 @@ def get_output_files(g: Grid, T: float, n_mean: int, n_profile: int) -> tuple:
95
94
 
96
95
  def close_output_files(f_mean: TextIO, f_profiles: list[TextIO] = None):
97
96
  """
98
- Close all output files
97
+ Close all output files.
99
98
 
100
99
  Args:
101
100
  f_mean: file handler for storing m space integral over time
llg3d/parameters.py CHANGED
@@ -58,13 +58,13 @@ parameters: dict[str, Parameter] = {
58
58
 
59
59
  def get_parameter_list(parameters: dict) -> str:
60
60
  """
61
- Returns parameter values as a string
61
+ Returns parameter values as a string.
62
62
 
63
63
  Args:
64
- d: Dictionary of parameters parsed by argparse
64
+ parameters: Dictionary of parameters parsed by argparse
65
65
 
66
66
  Returns:
67
- str: Formatted string of parameters
67
+ Formatted string of parameters
68
68
  """
69
69
  width = max([len(name) for name in parameters])
70
70
  s = ""
llg3d/post/__init__.py CHANGED
@@ -1 +1 @@
1
- """Post-processing tools for LLG3D."""
1
+ """Post-processing tools for LLG3D."""
@@ -1,5 +1,5 @@
1
1
  """
2
- Plot 1D curves from several files
2
+ Plot 1D curves from several files.
3
3
 
4
4
  Usage:
5
5
 
@@ -25,7 +25,6 @@ def plot(*files: tuple[str], output_file: str = DEFAULT_OUTPUT_FILE):
25
25
  files (tuple[str]): Paths to the result files.
26
26
  output_file (str): Path to the output image file.
27
27
  """
28
-
29
28
  fig, ax = plt.subplots()
30
29
  for file in files:
31
30
  if not file.endswith(".txt"):
@@ -43,12 +42,9 @@ def plot(*files: tuple[str], output_file: str = DEFAULT_OUTPUT_FILE):
43
42
 
44
43
 
45
44
  def main():
46
- parser = argparse.ArgumentParser(
47
- description="Plot results from one or more files."
48
- )
49
- parser.add_argument(
50
- "files", nargs="+", type=str, help="Path to the result files."
51
- )
45
+ """Main function to parse arguments and call the plot function."""
46
+ parser = argparse.ArgumentParser(description="Plot results from one or more files.")
47
+ parser.add_argument("files", nargs="+", type=str, help="Path to the result files.")
52
48
  parser.add_argument(
53
49
  "--output",
54
50
  "-o",
llg3d/post/process.py CHANGED
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Post-processes a set of runs grouped into a `run.json` file or
4
- into a set of SLURM job arrays:
3
+ Post-processes a set of runs.
4
+
5
+ Runs are grouped into a `run.json` file or into a set of SLURM job arrays:
5
6
 
6
7
  1. Extracts result data,
7
8
  2. Plots the computed average magnetization against temperature,
8
9
  3. Interpolates the computed points using cubic splines,
9
- 4. Determines the Curie temperature as the value corresponding to the minimal (negative) slope of the interpolated curve.
10
+ 4. Determines the Curie temperature as the value corresponding to the minimal (negative)
11
+ slope of the interpolated curve.
10
12
  """
11
13
 
12
14
  import json
@@ -17,14 +19,11 @@ from scipy.interpolate import interp1d
17
19
 
18
20
 
19
21
  class MagData:
20
- """
21
- Class to handle magnetization data and interpolation according to temperature
22
- """
22
+ """Class to handle magnetization data and interpolation according to temperature."""
23
23
 
24
24
  n_interp = 200
25
25
 
26
26
  def __init__(self, job_dir: Path = None, run_file: Path = Path("run.json")) -> None:
27
-
28
27
  if job_dir:
29
28
  self.parentpath = job_dir
30
29
  data, self.run = self.process_slurm_jobs()
@@ -65,16 +64,18 @@ class MagData:
65
64
  run = json.load(f)
66
65
  # Adding temperature and averaging value to the data list
67
66
  data.extend(
68
- [[float(T), res["m1_mean"]] for T, res in run["results"].items()]
67
+ [
68
+ [float(T), res["m1_mean"]]
69
+ for T, res in run["results"].items()
70
+ ]
69
71
  )
70
72
  except FileNotFoundError:
71
- print(f"Warning: {json_filename} file not found " f"in {jobdir.as_posix()}")
73
+ print(f"Warning: {json_filename} file not found in {jobdir.as_posix()}")
72
74
 
73
75
  data.sort() # Sorting by increasing temperatures
74
76
 
75
77
  return np.array(data), run
76
78
 
77
-
78
79
  def process_json(json_filepath: Path) -> tuple[np.array, dict]:
79
80
  """
80
81
  Reads the run.json file and extracts result data.
@@ -98,8 +99,12 @@ class MagData:
98
99
  @property
99
100
  def T_Curie(self) -> float:
100
101
  """
101
- Return the Curie temperature defined as the temperature at
102
- which the magnetization is below 0.1
102
+ Return the Curie temperature.
103
+
104
+ It is defined as the temperature at which the magnetization is below 0.1.
105
+
106
+ Returns:
107
+ float: Curie temperature
103
108
  """
104
109
  i_max = np.where(0.1 - self.interp(self.T) > 0)[0].min()
105
- return self.T[i_max]
110
+ return self.T[i_max]
llg3d/post/temperature.py CHANGED
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """
3
- Plot the magnetization vs temperature and determine the Curie temperature.
4
- """
2
+ """Plot the magnetization vs temperature and determine the Curie temperature."""
5
3
 
6
4
  import argparse
7
5
  from pathlib import Path
@@ -13,17 +11,14 @@ from .process import MagData
13
11
 
14
12
  def plot_m_vs_T(m: MagData, show: bool):
15
13
  """
16
- Plots the data (T, <m>), interpolates the values,
17
- calculates the Curie temperature.
18
- Exports to PNG.
14
+ Plots the data (T, <m>).
15
+
16
+ Interpolates the values, calculates the Curie temperature, exports to PNG.
19
17
 
20
18
  Args:
21
- data: numpy array (T, <m>)
22
- parentdir: path to the directory containing the runs
23
- run: descriptive dictionary of the run
19
+ m: Magnetization data object
24
20
  show: display the graph in a graphical window
25
21
  """
26
-
27
22
  print(f"T_Curie = {m.T_Curie:.0f} K")
28
23
 
29
24
  fig, ax = plt.subplots()
@@ -56,9 +51,7 @@ def plot_m_vs_T(m: MagData, show: bool):
56
51
 
57
52
 
58
53
  def main():
59
- """
60
- Parses the command line to execute processing functions
61
- """
54
+ """Parses the command line to execute processing functions."""
62
55
  parser = argparse.ArgumentParser(description=__doc__)
63
56
  parser.add_argument("--job_dir", type=Path, help="Slurm main job directory")
64
57
  parser.add_argument(
llg3d/simulation.py CHANGED
@@ -23,17 +23,14 @@ from .output import write_json
23
23
  class Simulation:
24
24
  """
25
25
  Class to encapsulate the simulation logic.
26
+
27
+ Args:
28
+ params: Dictionary of simulation parameters.
26
29
  """
27
30
 
28
31
  json_file = "run.json" #: JSON file to store the results
29
32
 
30
33
  def __init__(self, params: dict[str, Parameter]):
31
- """
32
- Initializes the simulation with parameters.
33
-
34
- Args:
35
- params: Dictionary of simulation parameters.
36
- """
37
34
  self.params: dict[str, Parameter] = params.copy() #: simulation parameters
38
35
  self.simulate: callable = self._get_simulate_function_from_name(
39
36
  self.params["solver"]
@@ -46,9 +43,7 @@ class Simulation:
46
43
  self.params["element_class"] = get_element_class(params["element"])
47
44
 
48
45
  def run(self):
49
- """
50
- Runs the simulation and store the results.
51
- """
46
+ """Runs the simulation and store the results."""
52
47
  self.total_time, self.filenames, self.m1_mean = self.simulate(**self.params)
53
48
 
54
49
  def _get_simulate_function_from_name(self, name: str) -> callable:
@@ -62,19 +57,15 @@ class Simulation:
62
57
  callable: The simulation function
63
58
 
64
59
  Example:
65
-
66
60
  >>> simulate = self.get_simulate_function_from_name("mpi")
67
61
 
68
62
  Will return the `simulate` function from the `llg3d.solver.mpi` module.
69
63
  """
70
-
71
64
  module = __import__(f"llg3d.solver.{name}", fromlist=["simulate"])
72
65
  return inspect.getattr_static(module, "simulate")
73
66
 
74
67
  def save(self):
75
- """
76
- Saves the results of the simulation to a JSON file.
77
- """
68
+ """Saves the results of the simulation to a JSON file."""
78
69
  params = self.params.copy() # save the parameters
79
70
  del params["element_class"] # remove class object before serialization
80
71
  if rank == 0:
llg3d/solver/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- Solver module for LLG3D
2
+ Solver module for LLG3D.
3
3
 
4
4
  This module contains different solver implementations.
5
5
  """
@@ -42,4 +42,4 @@ else:
42
42
 
43
43
  status = DummyStatus()
44
44
 
45
- from . import numpy, solver
45
+ from . import numpy, solver # noqa: E402
llg3d/solver/jax.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- LLG3D solver using XLA compilation
3
- """
1
+ """LLG3D solver using XLA compilation."""
4
2
 
5
3
  import os
6
4
  import time
@@ -20,8 +18,8 @@ def compute_H_anisotropy(
20
18
  m: jnp.ndarray, coeff_2: float, anisotropy: int
21
19
  ) -> jnp.ndarray:
22
20
  """
23
- Compute anisotropy field (JIT compiled)
24
-
21
+ Compute anisotropy field (JIT compiled).
22
+
25
23
  Args:
26
24
  m: Magnetization array (shape (3, nx, ny, nz))
27
25
  coeff_2: Coefficient for anisotropy
@@ -30,7 +28,6 @@ def compute_H_anisotropy(
30
28
  Returns:
31
29
  Anisotropy field array (shape (3, nx, ny, nz))
32
30
  """
33
-
34
31
  m1, m2, m3 = m
35
32
 
36
33
  m1m1 = m1 * m1
@@ -69,11 +66,17 @@ def compute_H_anisotropy(
69
66
 
70
67
  @jax.jit
71
68
  def laplacian3D(
72
- m_i: jnp.ndarray, dx2_inv: float, dy2_inv: float, dz2_inv: float, center_coeff: float
69
+ m_i: jnp.ndarray,
70
+ dx2_inv: float,
71
+ dy2_inv: float,
72
+ dz2_inv: float,
73
+ center_coeff: float,
73
74
  ) -> jnp.ndarray:
74
75
  """
75
- Compute Laplacian for a single component with Neumann boundary conditions (JIT compiled)
76
-
76
+ Compute Laplacian for a single component with Neumann boundary conditions.
77
+
78
+ (JIT compiled)
79
+
77
80
  Args:
78
81
  m_i: Single component of magnetization (shape (nx, ny, nz))
79
82
  dx2_inv: Inverse of squared grid spacing in x direction
@@ -94,12 +97,10 @@ def laplacian3D(
94
97
 
95
98
 
96
99
  @jax.jit
97
- def compute_laplacian(
98
- m: jnp.ndarray, dx: float, dy: float, dz: float
99
- ) -> jnp.ndarray:
100
+ def compute_laplacian(m: jnp.ndarray, dx: float, dy: float, dz: float) -> jnp.ndarray:
100
101
  """
101
- Compute 3D Laplacian with Neumann boundary conditions (JIT compiled)
102
-
102
+ Compute 3D Laplacian with Neumann boundary conditions (JIT compiled).
103
+
103
104
  Args:
104
105
  m: Magnetization array (shape (3, nx, ny, nz))
105
106
  dx: Grid spacing in x direction
@@ -111,7 +112,7 @@ def compute_laplacian(
111
112
  """
112
113
  dx2_inv, dy2_inv, dz2_inv = 1 / dx**2, 1 / dy**2, 1 / dz**2
113
114
  center_coeff = -2 * (dx2_inv + dy2_inv + dz2_inv)
114
-
115
+
115
116
  return jnp.stack(
116
117
  [
117
118
  laplacian3D(m[0], dx2_inv, dy2_inv, dz2_inv, center_coeff),
@@ -125,44 +126,44 @@ def compute_laplacian(
125
126
  @jax.jit
126
127
  def compute_space_average_jax(m1: jnp.ndarray) -> float:
127
128
  """
128
- Compute space average using midpoint method on GPU (JIT compiled)
129
-
129
+ Compute space average using midpoint method on GPU (JIT compiled).
130
+
130
131
  Args:
131
132
  m1: First component of magnetization (shape (nx, ny, nz))
132
-
133
+
133
134
  Returns:
134
135
  Space average of m1
135
136
  """
136
137
  # Get dimensions directly from the array shape
137
138
  Jx, Jy, Jz = m1.shape
138
-
139
+
139
140
  # Create 3D coordinate grids using the shape
140
141
  i_coords = jnp.arange(Jx)
141
- j_coords = jnp.arange(Jy)
142
+ j_coords = jnp.arange(Jy)
142
143
  k_coords = jnp.arange(Jz)
143
-
144
+
144
145
  # Create 3D coordinate grids
145
- ii, jj, kk = jnp.meshgrid(i_coords, j_coords, k_coords, indexing='ij')
146
-
146
+ ii, jj, kk = jnp.meshgrid(i_coords, j_coords, k_coords, indexing="ij")
147
+
147
148
  # Apply midpoint weights (0.5 on edges, 1.0 elsewhere)
148
149
  weights = jnp.ones_like(m1)
149
- weights = jnp.where((ii == 0) | (ii == Jx-1), weights * 0.5, weights)
150
- weights = jnp.where((jj == 0) | (jj == Jy-1), weights * 0.5, weights)
151
- weights = jnp.where((kk == 0) | (kk == Jz-1), weights * 0.5, weights)
152
-
150
+ weights = jnp.where((ii == 0) | (ii == Jx - 1), weights * 0.5, weights)
151
+ weights = jnp.where((jj == 0) | (jj == Jy - 1), weights * 0.5, weights)
152
+ weights = jnp.where((kk == 0) | (kk == Jz - 1), weights * 0.5, weights)
153
+
153
154
  # Compute weighted sum and normalize
154
155
  weighted_sum = jnp.sum(weights * m1)
155
-
156
+
156
157
  # Compute ncell from the weights (this is the effective cell count)
157
158
  ncell = jnp.sum(weights)
158
-
159
+
159
160
  return weighted_sum / ncell
160
161
 
161
162
 
162
163
  @jax.jit
163
164
  def cross_product(a: jnp.ndarray, b: jnp.ndarray) -> jnp.ndarray:
164
165
  r"""
165
- Compute cross product :math:`a \times b` (JIT compiled)
166
+ Compute cross product :math:`a \times b` (JIT compiled).
166
167
 
167
168
  Args:
168
169
  a: First vector (shape (3, nx, ny, nz))
@@ -181,11 +182,12 @@ def compute_slope(
181
182
  g_params: dict, e_params: dict, m: jnp.ndarray, R_random: jnp.ndarray
182
183
  ) -> jnp.ndarray:
183
184
  """
184
- JIT-compiled version of compute_slope_jax using modular sub-functions
185
+ JIT-compiled version of compute_slope_jax using modular sub-functions.
185
186
 
186
187
  Args:
187
188
  g_params: Grid parameters dict (dx, dy, dz)
188
- e_params: Element parameters dict (coeff_1, coeff_2, coeff_3, lambda_G, anisotropy)
189
+ e_params: Element parameters dict (coeff_1, coeff_2, coeff_3, lambda_G,
190
+ anisotropy)
189
191
  m: Magnetization array (shape (3, nx, ny, nz))
190
192
  R_random: Random field array (shape (3, nx, ny, nz))
191
193
 
@@ -234,7 +236,7 @@ def simulate(
234
236
  **_,
235
237
  ) -> tuple[float, str, float]:
236
238
  """
237
- Simulates the system for N iterations using JAX
239
+ Simulates the system for N iterations using JAX.
238
240
 
239
241
  Args:
240
242
  N: Number of iterations
@@ -258,7 +260,6 @@ def simulate(
258
260
  - The output filenames
259
261
  - The average magnetization
260
262
  """
261
-
262
263
  # Configure JAX
263
264
  if device == "auto":
264
265
  # Let JAX choose the best available device
@@ -276,7 +277,10 @@ def simulate(
276
277
  os.environ["CUDA_VISIBLE_DEVICES"] = str(gpu_id)
277
278
  print(f"Set CUDA_VISIBLE_DEVICES={gpu_id}")
278
279
  else:
279
- print(f"Using external CUDA_VISIBLE_DEVICES={os.environ['CUDA_VISIBLE_DEVICES']}")
280
+ cuda_visible_devices = os.environ["CUDA_VISIBLE_DEVICES"]
281
+ print(
282
+ f"Using external CUDA_VISIBLE_DEVICES={cuda_visible_devices}"
283
+ )
280
284
 
281
285
  # Set precision
282
286
  if precision == "double":
@@ -305,11 +309,11 @@ def simulate(
305
309
 
306
310
  # --- Initialization ---
307
311
  def theta_init(shape):
308
- """Initialization of theta"""
312
+ """Initialization of theta."""
309
313
  return jnp.zeros(shape, dtype=jnp_float)
310
314
 
311
315
  def phi_init(t, shape):
312
- """Initialization of phi"""
316
+ """Initialization of phi."""
313
317
  return jnp.zeros(shape, dtype=jnp_float) + e.gamma_0 * H_ext * t
314
318
 
315
319
  m_n = jnp.zeros((3,) + dims, dtype=jnp_float)
@@ -328,16 +332,16 @@ def simulate(
328
332
 
329
333
  # === JIT WARMUP: Pre-compile all functions to exclude compilation time ===
330
334
  print("Warming up JIT compilation...")
331
-
335
+
332
336
  # Generate dummy random field for warmup
333
337
  warmup_key = random.PRNGKey(42)
334
338
  R_warmup = e.coeff_4 * random.normal(warmup_key, (3,) + dims, dtype=jnp_float)
335
-
339
+
336
340
  # Warmup all JIT functions with actual data shapes
337
341
  _ = compute_slope(g_params, e_params, m_n, R_warmup)
338
342
  if n_mean != 0:
339
343
  _ = compute_space_average_jax(m_n[0])
340
-
344
+
341
345
  # Force compilation and execution to complete
342
346
  jax.block_until_ready(m_n)
343
347
  print("JIT warmup completed.")
llg3d/solver/mpi.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- LLG3D Solver using MPI
2
+ LLG3D Solver using MPI.
3
3
 
4
4
  The parallelization is done in the x direction.
5
5
  """
@@ -22,9 +22,9 @@ def get_boundaries_x(
22
22
  g: Grid, m: np.ndarray, blocking: bool = False
23
23
  ) -> tuple[np.ndarray, np.ndarray, MPI.Request, MPI.Request]:
24
24
  """
25
- Returns the boundaries asynchronously:
26
- allows overlapping communication time of boundaries
27
- with calculations
25
+ Returns the boundaries asynchronously.
26
+
27
+ Allows overlapping communication time of boundaries with calculations.
28
28
 
29
29
  Args:
30
30
  g: Grid object
@@ -37,7 +37,6 @@ def get_boundaries_x(
37
37
  - request_start: Request for start boundary
38
38
  - request_end: Request for end boundary
39
39
  """
40
-
41
40
  # Extract slices for Neumann boundary conditions
42
41
 
43
42
  m_i_x_start = np.empty((1, g.Jy, g.Jz))
@@ -80,6 +79,7 @@ def laplacian3D(
80
79
  ) -> np.ndarray:
81
80
  """
82
81
  Returns the Laplacian of m_i in 3D.
82
+
83
83
  We start by calculating contributions in y and z, to wait
84
84
  for the end of communications in x.
85
85
 
@@ -97,7 +97,6 @@ def laplacian3D(
97
97
  Returns:
98
98
  Laplacian of m_i (shape (nx, ny, nz))
99
99
  """
100
-
101
100
  # Extract slices for Neumann boundary conditions
102
101
  m_i_y_start = m_i[:, 1:2, :]
103
102
  m_i_y_end = m_i[:, -2:-1, :]
@@ -143,7 +142,7 @@ def compute_laplacian(
143
142
  boundaries: tuple[np.ndarray, np.ndarray, MPI.Request, MPI.Request],
144
143
  ) -> np.ndarray:
145
144
  """
146
- Compute the laplacian of m in 3D
145
+ Compute the laplacian of m in 3D.
147
146
 
148
147
  Args:
149
148
  g: Grid object
@@ -153,7 +152,6 @@ def compute_laplacian(
153
152
  Returns:
154
153
  Laplacian of m (shape (3, nx, ny, nz))
155
154
  """
156
-
157
155
  dx2_inv, dy2_inv, dz2_inv, center_coeff = g.get_laplacian_coeff()
158
156
 
159
157
  return np.stack(
@@ -174,7 +172,7 @@ def compute_slope(
174
172
  boundaries: tuple[np.ndarray, np.ndarray, MPI.Request, MPI.Request],
175
173
  ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
176
174
  """
177
- Compute the slope of the LLG equation
175
+ Compute the slope of the LLG equation.
178
176
 
179
177
  Args:
180
178
  g: Grid object
@@ -186,7 +184,6 @@ def compute_slope(
186
184
  Returns:
187
185
  Slope array (shape (3, nx, ny, nz))
188
186
  """
189
-
190
187
  # Precalculate terms used multiple times
191
188
 
192
189
  H_aniso = compute_H_anisotropy(e, m)
@@ -205,8 +202,8 @@ def compute_slope(
205
202
 
206
203
  def space_average(g: Grid, m: np.ndarray) -> float:
207
204
  """
208
- Returns the spatial average of m with shape (g.dims)
209
- using the midpoint method.
205
+ Returns the spatial average of m with shape (g.dims) using the midpoint method.
206
+
210
207
  Performs the local sum on each process and then reduces it to process 0.
211
208
 
212
209
  Args:
@@ -216,7 +213,6 @@ def space_average(g: Grid, m: np.ndarray) -> float:
216
213
  Returns:
217
214
  Spatial average of m
218
215
  """
219
-
220
216
  # Make a copy of m to avoid modifying its value
221
217
  mm = m.copy()
222
218
 
@@ -242,16 +238,14 @@ def space_average(g: Grid, m: np.ndarray) -> float:
242
238
 
243
239
  def integral_yz(m: np.ndarray) -> np.ndarray:
244
240
  """
245
- Returns the spatial average of shape (g.dims[0],)
246
- in y and z of m of shape (g.dims) using the midpoint method
241
+ Returns the spatial average of m using the midpoint method along y and z.
247
242
 
248
243
  Args:
249
244
  m: Array to be integrated
250
245
 
251
246
  Returns:
252
- np.ndarray: Spatial average of m in y and z
247
+ np.ndarray: Spatial average of m in y and z of shape (g.dims[0],)
253
248
  """
254
-
255
249
  # Make a copy of m to avoid modifying its value
256
250
  mm = m.copy()
257
251
 
@@ -267,26 +261,25 @@ def integral_yz(m: np.ndarray) -> np.ndarray:
267
261
 
268
262
  def profile(m: np.ndarray, m_xprof: np.ndarray):
269
263
  """
270
- Retrieves the x profile of the average of m in y and z
264
+ Retrieves the x profile of the average of m in y and z.
271
265
 
272
266
  Args:
273
267
  m: Array to be integrated
274
268
  m_xprof: Array to store the x profile
275
269
  """
276
-
277
270
  # Gather m in mglob
278
271
  m_mean_yz = integral_yz(m)
279
272
  comm.Gather(m_mean_yz, m_xprof)
280
273
 
281
274
 
282
275
  def theta_init(t: float, g: Grid) -> np.ndarray:
283
- """Initialization of theta"""
276
+ """Initialization of theta."""
284
277
  x, y, z = g.get_mesh(local=True)
285
278
  return np.zeros(g.dims)
286
279
 
287
280
 
288
281
  def phi_init(t: float, g: Grid, e: Element) -> np.ndarray:
289
- """Initialization of phi"""
282
+ """Initialization of phi."""
290
283
  # return np.zeros(shape) + e.coeff_3 * t
291
284
  return np.zeros(g.dims) + e.gamma_0 * e.H_ext * t
292
285
 
@@ -309,7 +302,7 @@ def simulate(
309
302
  **_,
310
303
  ) -> tuple[float, str, float]:
311
304
  """
312
- Simulates the system for N iterations
305
+ Simulates the system for N iterations.
313
306
 
314
307
  Args:
315
308
  N: Number of iterations
@@ -332,7 +325,6 @@ def simulate(
332
325
  - The time taken for the simulation
333
326
  - The output filename
334
327
  """
335
-
336
328
  if Jx % size != 0:
337
329
  if rank == 0:
338
330
  print(
@@ -433,7 +425,7 @@ def simulate(
433
425
 
434
426
 
435
427
  class ArgumentParser(argparse.ArgumentParser):
436
- """An argument parser compatible with MPI"""
428
+ """An argument parser compatible with MPI."""
437
429
 
438
430
  def _print_message(self, message, file=None):
439
431
  if rank == 0 and message:
@@ -442,6 +434,15 @@ class ArgumentParser(argparse.ArgumentParser):
442
434
  file.write(message)
443
435
 
444
436
  def exit(self, status=0, message=None):
437
+ """
438
+ Exit the program using MPI finalize.
439
+
440
+ Args:
441
+ status: Exit status code
442
+ message: Optional exit message
443
+ file: Output file (default: stderr)
444
+
445
+ """
445
446
  if message:
446
447
  self._print_message(message, sys.stderr)
447
448
  comm.barrier()
llg3d/solver/numpy.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- LLG3D Solver using NumPy
3
- """
1
+ """LLG3D Solver using NumPy."""
4
2
 
5
3
  import time
6
4
 
@@ -16,7 +14,7 @@ def laplacian3D(
16
14
  m_i: np.ndarray, dx2_inv: float, dy2_inv: float, dz2_inv: float, center_coeff: float
17
15
  ) -> np.ndarray:
18
16
  """
19
- Returns the laplacian of m in 3D
17
+ Returns the laplacian of m in 3D.
20
18
 
21
19
  Args:
22
20
  m_i: Magnetization array (shape (nx, ny, nz))
@@ -28,7 +26,6 @@ def laplacian3D(
28
26
  Returns:
29
27
  Laplacian of m (shape (nx, ny, nz))
30
28
  """
31
-
32
29
  m_i_padded = np.pad(m_i, ((1, 1), (1, 1), (1, 1)), mode="reflect")
33
30
 
34
31
  laplacian = (
@@ -42,7 +39,7 @@ def laplacian3D(
42
39
 
43
40
  def compute_laplacian(g: Grid, m: np.ndarray) -> np.ndarray:
44
41
  """
45
- Compute the laplacian of m in 3D
42
+ Compute the laplacian of m in 3D.
46
43
 
47
44
  Args:
48
45
  g: Grid object
@@ -51,7 +48,6 @@ def compute_laplacian(g: Grid, m: np.ndarray) -> np.ndarray:
51
48
  Returns:
52
49
  Laplacian of m (shape (3, nx, ny, nz))
53
50
  """
54
-
55
51
  dx2_inv, dy2_inv, dz2_inv, center_coeff = g.get_laplacian_coeff()
56
52
 
57
53
  return np.stack(
@@ -68,17 +64,17 @@ def compute_slope(
68
64
  g: Grid, e: Element, m: np.ndarray, R_random: np.ndarray
69
65
  ) -> np.ndarray:
70
66
  """
71
- Compute the slope of the LLG equation
67
+ Compute the slope of the LLG equation.
68
+
72
69
  Args:
73
70
  g: Grid object
74
71
  e: Element object
75
72
  m: Magnetization array (shape (3, nx, ny, nz))
76
- R_random: Random field array (shape (3, nx, ny, nz))
73
+ R_random: Random field array (shape (3, nx, ny, nz)).
77
74
 
78
75
  Returns:
79
76
  Slope array (shape (3, nx, ny, nz))
80
77
  """
81
-
82
78
  H_aniso = compute_H_anisotropy(e, m)
83
79
 
84
80
  laplacian_m = compute_laplacian(g, m)
@@ -111,13 +107,14 @@ def simulate(
111
107
  **_,
112
108
  ) -> tuple[float, str, float]:
113
109
  """
114
- Simulates the system for N iterations
110
+ Simulates the system for N iterations.
115
111
 
116
112
  Args:
117
113
  N: Number of iterations
118
114
  Jx: Number of grid points in x direction
119
115
  Jy: Number of grid points in y direction
120
- Jz: Number of grid points in z direction dx: Grid spacing
116
+ Jz: Number of grid points in z direction
117
+ dx: Grid spacing
121
118
  T: Temperature in Kelvin
122
119
  H_ext: External magnetic field strength
123
120
  dt: Time step for the simulation
@@ -152,11 +149,11 @@ def simulate(
152
149
  # --- Initialization ---
153
150
 
154
151
  def theta_init(shape):
155
- """Initialization of theta"""
152
+ """Initialization of theta."""
156
153
  return np.zeros(shape, dtype=np_float)
157
154
 
158
155
  def phi_init(t, shape):
159
- """Initialization of phi"""
156
+ """Initialization of phi."""
160
157
  return np.zeros(shape, dtype=np_float) + e.gamma_0 * H_ext * t
161
158
 
162
159
  m_n = np.zeros((3,) + dims, dtype=np_float)
llg3d/solver/opencl.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- LLG3D Solver using OpenCL
3
- """
1
+ """LLG3D Solver using OpenCL."""
4
2
 
5
3
  import time
6
4
  from pathlib import Path
@@ -15,15 +13,17 @@ from ..grid import Grid
15
13
  from ..element import Element, Cobalt
16
14
 
17
15
 
18
- def get_context_and_device(device_selection: str = "auto") -> tuple[cl.Context, cl.Device]:
16
+ def get_context_and_device(
17
+ device_selection: str = "auto",
18
+ ) -> tuple[cl.Context, cl.Device]:
19
19
  """
20
- Get the OpenCL context and device
20
+ Get the OpenCL context and device.
21
21
 
22
22
  Args:
23
23
  device_selection:
24
-
24
+
25
25
  - ``"auto"``: Let OpenCL choose automatically
26
- - ``"cpu"``: Select CPU device
26
+ - ``"cpu"``: Select CPU device
27
27
  - ``"gpu"``: Select first available GPU
28
28
  - ``"gpu:N"``: Select specific GPU by index (e.g., ``"gpu:0"``, ``"gpu:1"``)
29
29
 
@@ -35,17 +35,17 @@ def get_context_and_device(device_selection: str = "auto") -> tuple[cl.Context,
35
35
  context = cl.create_some_context(interactive=False)
36
36
  device = context.devices[0]
37
37
  return context, device
38
-
38
+
39
39
  # Get all platforms and devices
40
40
  platforms = cl.get_platforms()
41
41
  all_devices = []
42
-
42
+
43
43
  for platform in platforms:
44
44
  all_devices.extend(platform.get_devices())
45
-
45
+
46
46
  if not all_devices:
47
47
  raise RuntimeError("No OpenCL devices found")
48
-
48
+
49
49
  # Filter devices based on selection
50
50
  if device_selection == "cpu":
51
51
  cpu_devices = [d for d in all_devices if d.type & cl.device_type.CPU]
@@ -61,26 +61,29 @@ def get_context_and_device(device_selection: str = "auto") -> tuple[cl.Context,
61
61
  gpu_devices = [d for d in all_devices if d.type & cl.device_type.GPU]
62
62
  if not gpu_devices:
63
63
  raise RuntimeError("No GPU devices found")
64
-
64
+
65
65
  gpu_index = int(device_selection.split(":")[1])
66
66
  if gpu_index >= len(gpu_devices):
67
- raise RuntimeError(f"GPU index {gpu_index} not available. Found {len(gpu_devices)} GPU(s)")
67
+ raise RuntimeError(
68
+ f"GPU index {gpu_index} not available. Found {len(gpu_devices)} GPU(s)"
69
+ )
68
70
  selected_device = gpu_devices[gpu_index]
69
71
  else:
70
72
  raise ValueError(f"Invalid device selection: {device_selection}")
71
-
73
+
72
74
  # Create context with selected device
73
75
  context = cl.Context([selected_device])
74
76
  print(f"Selected OpenCL device: {selected_device.name} ({selected_device.type})")
75
-
77
+
76
78
  return context, selected_device
77
79
 
78
80
 
79
81
  def get_precision(device: cl.Device, precision: str) -> np.dtype:
80
82
  """
81
- Get the numpy float type based on the precision
83
+ Get the numpy float type based on the precision.
82
84
 
83
85
  Args:
86
+ device: OpenCL device
84
87
  precision: Precision of the simulation (single or double)
85
88
 
86
89
  Returns:
@@ -97,9 +100,7 @@ def get_precision(device: cl.Device, precision: str) -> np.dtype:
97
100
 
98
101
 
99
102
  class Program:
100
- """
101
- Class to manage the OpenCL kernels for the LLG3D simulation
102
- """
103
+ """Class to manage the OpenCL kernels for the LLG3D simulation."""
103
104
 
104
105
  def __init__(self, g: Grid, context: cl.Context, np_float: np.dtype):
105
106
  self.grid = g
@@ -109,12 +110,11 @@ class Program:
109
110
 
110
111
  def _get_built_program(self) -> cl.Program:
111
112
  """
112
- Return the OpenCL program built from the source code
113
+ Return the OpenCL program built from the source code.
113
114
 
114
115
  Returns:
115
116
  The OpenCL program object
116
117
  """
117
-
118
118
  opencl_code = (Path(__file__).parent / "llg3d.cl").read_text()
119
119
  build_options = "-D USE_DOUBLE_PRECISION" if self.np_float == np.float64 else ""
120
120
  build_options += (
@@ -157,13 +157,13 @@ def simulate(
157
157
  **_,
158
158
  ) -> tuple[float, str, float]:
159
159
  """
160
- Simulates the system over N iterations
160
+ Simulates the system over N iterations.
161
161
 
162
162
  Args:
163
163
  N: Number of iterations
164
164
  Jx: Number of grid points in x direction
165
165
  Jy: Number of grid points in y direction
166
- Jz: Number of grid points in z direction
166
+ Jz: Number of grid points in z direction
167
167
  dx: Grid spacing
168
168
  T: Temperature in Kelvin
169
169
  H_ext: External magnetic field strength
@@ -181,7 +181,6 @@ def simulate(
181
181
  - The time taken for the simulation
182
182
  - The output filename
183
183
  """
184
-
185
184
  context, opencl_device = get_context_and_device(device)
186
185
  np_float = get_precision(opencl_device, precision)
187
186
 
@@ -190,17 +189,19 @@ def simulate(
190
189
 
191
190
  e = element_class(T, H_ext, g, dt)
192
191
  if not isinstance(e, Cobalt):
193
- raise NotImplementedError(f"Element is {type(e)} but only {Cobalt} is supported at the moment.")
192
+ raise NotImplementedError(
193
+ f"Element is {type(e)} but only {Cobalt} is supported at the moment."
194
+ )
194
195
  print(f"CFL = {e.get_CFL()}")
195
196
 
196
197
  # --- Initialization ---
197
198
 
198
199
  def theta_init(shape):
199
- """Initialization of theta"""
200
+ """Initialization of theta."""
200
201
  return np.zeros(shape, dtype=np_float)
201
202
 
202
203
  def phi_init(t, shape):
203
- """Initialization of phi"""
204
+ """Initialization of phi."""
204
205
  return np.zeros(shape, dtype=np_float) + e.gamma_0 * H_ext * t
205
206
 
206
207
  m_n = np.zeros((3,) + g.dims, dtype=np_float)
@@ -219,7 +220,7 @@ def simulate(
219
220
  update_1_kernel = program.get_kernel("update_1", [None] * 3 + [np_float])
220
221
  update_2_kernel = program.get_kernel("update_2", [None] * 4 + [np_float])
221
222
  normalize_kernel = program.get_kernel("normalize")
222
-
223
+
223
224
  # Create a CL array for m1 component in order to compute averages
224
225
  d_m1 = clarray.empty(queue, g.ntot, np_float)
225
226
  copy_m1_kernel = program.get_kernel("copy_m1", [None, None])
@@ -309,11 +310,11 @@ def simulate(
309
310
  # Copy only the first component from d_m_n to d_m1 with weights applied
310
311
  # d_m_n contains [m1, m2, m3] interleaved, we want only m1
311
312
  copy_m1_kernel(queue, g.dims, None, d_m_n, d_m1.data)
312
-
313
+
313
314
  # Use PyOpenCL array sum to compute the weighted sum
314
315
  weighted_sum = clarray.sum(d_m1).get()
315
316
  m1_mean = weighted_sum / g.ncell
316
-
317
+
317
318
  if n >= start_averaging:
318
319
  m1_average += m1_mean * n_mean
319
320
 
llg3d/solver/solver.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- Common functions for the LLG3D solver
3
- """
1
+ """Common functions for the LLG3D solver."""
4
2
 
5
3
  import numpy as np
6
4
 
@@ -21,7 +19,6 @@ def cross_product(a: np.ndarray, b: np.ndarray) -> np.ndarray:
21
19
  Returns:
22
20
  Cross product :math:`a \times b` (shape (3, nx, ny, nz))
23
21
  """
24
-
25
22
  return np.stack(
26
23
  [
27
24
  a[1] * b[2] - a[2] * b[1], # x-component
@@ -34,14 +31,15 @@ def cross_product(a: np.ndarray, b: np.ndarray) -> np.ndarray:
34
31
 
35
32
  def compute_H_anisotropy(e: Element, m: np.ndarray) -> np.ndarray:
36
33
  """
37
- Compute the anisotropy field
34
+ Compute the anisotropy field.
35
+
38
36
  Args:
39
37
  e: Element object
40
- m: Magnetization array (shape (3, nx, ny, nz))
38
+ m: Magnetization array (shape (3, nx, ny, nz)).
39
+
41
40
  Returns:
42
41
  Anisotropy field array (shape (3, nx, ny, nz))
43
42
  """
44
-
45
43
  m1, m2, m3 = m
46
44
 
47
45
  m1m1 = m1 * m1
@@ -63,8 +61,7 @@ def compute_H_anisotropy(e: Element, m: np.ndarray) -> np.ndarray:
63
61
 
64
62
  def space_average(g: Grid, m: np.ndarray, copy: bool = True) -> float:
65
63
  """
66
- Returns the spatial average of m with shape (g.dims)
67
- using the midpoint method
64
+ Returns the spatial average of m with shape (g.dims) using the midpoint method.
68
65
 
69
66
  Args:
70
67
  g: Grid object
@@ -72,9 +69,8 @@ def space_average(g: Grid, m: np.ndarray, copy: bool = True) -> float:
72
69
  copy: If True, copy m to avoid modifying its value
73
70
 
74
71
  Returns:
75
- float: Spatial average of m
72
+ Spatial average of m
76
73
  """
77
-
78
74
  # copy m to avoid modifying its value
79
75
  mm = m.copy() if copy else m
80
76
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llg3d
3
- Version: 2.0.0
4
- Summary: Solveur pour l'équation de Landau-Lifshitz-Gilbert stochastique en 3D
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
@@ -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,,
@@ -1,25 +0,0 @@
1
- llg3d/__init__.py,sha256=7Bcs0le_-tmgNMoIH8KaKJRqHjrzfNLu6RMP6c8T-Jo,160
2
- llg3d/__main__.py,sha256=wZ2BSBj_ZgnfGNmYZkeDVrfMPpzLOEmjmSnXrjnwfzo,96
3
- llg3d/element.py,sha256=3umDR1CxRjMfaBKjDPp35RxG24cSagONwzWkY5doxuI,4290
4
- llg3d/grid.py,sha256=Vkj0K9lkYZLlmSk6-S3BOupKh6JtxrWTABiK06GZHsk,3844
5
- llg3d/main.py,sha256=A4d1_6NfPNfHptPoitE772zZT4akeF0zFVXM5Gu1Tjo,1829
6
- llg3d/output.py,sha256=llPZ7lQ1bKwfe-A_hBYEn0QWf0A2aFjHneJAv-djVJk,2921
7
- llg3d/parameters.py,sha256=Jg1sDPWLaUeDMFFJBdb3YmN7ljgsQ-8qDXMpXUUqjGI,2437
8
- llg3d/simulation.py,sha256=XtmB8vYaQbFsx9sQPXqGQs1m-iGTPqRpis1Y67W5hO0,3551
9
- llg3d/post/__init__.py,sha256=PUQUkQURiZrnZmfGWJK5YCqk5Wv1iqo_IzczftqgJVQ,38
10
- llg3d/post/plot_results.py,sha256=NEhIEpVouQO0kf5Gsq5CLsDtnQtBuC0hxQ8IWII04Fg,1507
11
- llg3d/post/process.py,sha256=yIM4LuhqSckfXCBTToPSnRS7qSswJ8tk3DNzJ-wQerU,3422
12
- llg3d/post/temperature.py,sha256=Zz7za7Af3zQAxuSVznpCf5Ive9da4dvOK-2U2-Vwhuc,2271
13
- llg3d/solver/__init__.py,sha256=cAXNyd8XZrdkfutNI0vgyMG4YueJiRL3Mqbf8nI7jvo,886
14
- llg3d/solver/jax.py,sha256=o-c8Vs6T6hAxn0eFchHwBjry8QOWr3MK-d2Rs7XpRRA,11965
15
- llg3d/solver/mpi.py,sha256=HINbUbVxLvqrM0yKMdg8sz_28eZ3TXOScaSNYvSgovg,12850
16
- llg3d/solver/numpy.py,sha256=vqQIMatv84obDcNjoCmKRtuRmOBxRAjwfxmKPH1h5YM,6035
17
- llg3d/solver/opencl.py,sha256=ZkZrjzsQryrYDqoJbGLq2E7uJHaxAeXJUEebOLaNlWw,10221
18
- llg3d/solver/solver.py,sha256=7m5iU1JtZfdRW6l20tnRElU4sPsmm-gqgNujyqe1BuE,2152
19
- llg3d-2.0.0.dist-info/licenses/AUTHORS,sha256=vhJ88HikYvOrGiB_l1xH2X6hyn9ZJafx6mpoMYNhh1I,297
20
- llg3d-2.0.0.dist-info/licenses/LICENSE,sha256=aFxTGAyyve8nM9T2jWTarJzQhdSSC3MbbN1heNAev9c,1062
21
- llg3d-2.0.0.dist-info/METADATA,sha256=4wnGvwrqKOLrZcsb5XIPKWBhWt1-diU3-f9LSUdbRP4,1952
22
- llg3d-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- llg3d-2.0.0.dist-info/entry_points.txt,sha256=kjuf09uDEuROnDeuHkVen3oAS4ThEPS084TpHmQAbJ8,133
24
- llg3d-2.0.0.dist-info/top_level.txt,sha256=cBZ0roaXt3CAXqYojuO84lGPCtWuLlXxLGLYRKmHZy0,6
25
- llg3d-2.0.0.dist-info/RECORD,,
File without changes