evoxels 0.1.2__tar.gz → 1.0.0__tar.gz
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.
- {evoxels-0.1.2 → evoxels-1.0.0}/LICENSE +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/PKG-INFO +5 -4
- {evoxels-0.1.2 → evoxels-1.0.0}/README.md +3 -3
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/__init__.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/boundary_conditions.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/fd_stencils.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/function_approximators.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/inversion.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/precompiled_solvers/__init__.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/precompiled_solvers/allen_cahn.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/precompiled_solvers/cahn_hilliard.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/problem_definition.py +2 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/profiler.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/solvers.py +112 -46
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/timesteppers.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/utils.py +8 -2
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/voxelfields.py +23 -13
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels/voxelgrid.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels.egg-info/PKG-INFO +5 -4
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels.egg-info/SOURCES.txt +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels.egg-info/dependency_links.txt +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels.egg-info/requires.txt +1 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/evoxels.egg-info/top_level.txt +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/pyproject.toml +3 -2
- {evoxels-0.1.2 → evoxels-1.0.0}/setup.cfg +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/tests/test_fields.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/tests/test_inversion.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/tests/test_laplace.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/tests/test_rhs.py +0 -0
- {evoxels-0.1.2 → evoxels-1.0.0}/tests/test_solvers.py +0 -0
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evoxels
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Differentiable physics framework for voxel-based microstructure simulations
|
|
5
5
|
Author-email: Simon Daubner <s.daubner@imperial.ac.uk>
|
|
6
6
|
License: MIT
|
|
@@ -40,6 +40,7 @@ Provides-Extra: notebooks
|
|
|
40
40
|
Requires-Dist: ipywidgets; extra == "notebooks"
|
|
41
41
|
Requires-Dist: ipympl; extra == "notebooks"
|
|
42
42
|
Requires-Dist: notebook; extra == "notebooks"
|
|
43
|
+
Requires-Dist: taufactor; extra == "notebooks"
|
|
43
44
|
Dynamic: license-file
|
|
44
45
|
|
|
45
46
|
[](https://github.com/daubners/evoxels/actions/workflows/python-package.yml)
|
|
@@ -81,7 +82,7 @@ TL;DR
|
|
|
81
82
|
```bash
|
|
82
83
|
conda create --name voxenv python=3.12
|
|
83
84
|
conda activate voxenv
|
|
84
|
-
pip install evoxels[torch,jax,dev,notebooks]
|
|
85
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
85
86
|
pip install --upgrade "jax[cuda12]"
|
|
86
87
|
```
|
|
87
88
|
|
|
@@ -103,7 +104,7 @@ Navigate to the evoxels folder, then
|
|
|
103
104
|
```
|
|
104
105
|
pip install -e .[torch] # install with torch backend
|
|
105
106
|
pip install -e .[jax] # install with jax backend
|
|
106
|
-
pip install -e .[dev,
|
|
107
|
+
pip install -e .[dev,notebooks] # install testing and notebooks
|
|
107
108
|
```
|
|
108
109
|
Note that the default `[jax]` installation is only CPU compatible. To install the corresponding CUDA libraries check your CUDA version with
|
|
109
110
|
```bash
|
|
@@ -115,7 +116,7 @@ pip install -U "jax[cuda12]"
|
|
|
115
116
|
```
|
|
116
117
|
To install both backends within one environment it is important to install torch first and then upgrade the `jax` installation e.g.
|
|
117
118
|
```bash
|
|
118
|
-
pip install evoxels[torch,
|
|
119
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
119
120
|
pip install --upgrade "jax[cuda12]"
|
|
120
121
|
```
|
|
121
122
|
To work with the example notebooks install Jupyter and all notebook related dependencies via
|
|
@@ -37,7 +37,7 @@ TL;DR
|
|
|
37
37
|
```bash
|
|
38
38
|
conda create --name voxenv python=3.12
|
|
39
39
|
conda activate voxenv
|
|
40
|
-
pip install evoxels[torch,jax,dev,notebooks]
|
|
40
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
41
41
|
pip install --upgrade "jax[cuda12]"
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -59,7 +59,7 @@ Navigate to the evoxels folder, then
|
|
|
59
59
|
```
|
|
60
60
|
pip install -e .[torch] # install with torch backend
|
|
61
61
|
pip install -e .[jax] # install with jax backend
|
|
62
|
-
pip install -e .[dev,
|
|
62
|
+
pip install -e .[dev,notebooks] # install testing and notebooks
|
|
63
63
|
```
|
|
64
64
|
Note that the default `[jax]` installation is only CPU compatible. To install the corresponding CUDA libraries check your CUDA version with
|
|
65
65
|
```bash
|
|
@@ -71,7 +71,7 @@ pip install -U "jax[cuda12]"
|
|
|
71
71
|
```
|
|
72
72
|
To install both backends within one environment it is important to install torch first and then upgrade the `jax` installation e.g.
|
|
73
73
|
```bash
|
|
74
|
-
pip install evoxels[torch,
|
|
74
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
75
75
|
pip install --upgrade "jax[cuda12]"
|
|
76
76
|
```
|
|
77
77
|
To work with the example notebooks install Jupyter and all notebook related dependencies via
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -120,6 +120,8 @@ class ReactionDiffusion(SemiLinearODE):
|
|
|
120
120
|
bc_fun = self.vg.bc.pad_zero_flux_periodic
|
|
121
121
|
self.pad_boundary = lambda field, bc0, bc1: bc_fun(field)
|
|
122
122
|
k_squared = self.vg.fft_k_squared_nonperiodic()
|
|
123
|
+
else:
|
|
124
|
+
raise ValueError(f"Unsupported BC type: {self.BC_type}")
|
|
123
125
|
|
|
124
126
|
self._fourier_symbol = -self.D * self.A * k_squared
|
|
125
127
|
|
|
File without changes
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from IPython.display import clear_output
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from typing import Callable, Any, Type
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
4
5
|
from timeit import default_timer as timer
|
|
5
6
|
import sys
|
|
6
7
|
from .problem_definition import ODE
|
|
7
8
|
from .timesteppers import TimeStepper
|
|
8
9
|
|
|
9
10
|
@dataclass
|
|
10
|
-
class
|
|
11
|
+
class BaseSolver(ABC):
|
|
11
12
|
"""Generic wrapper for solving one or more fields with a time stepper."""
|
|
12
13
|
vf: Any # VoxelFields object
|
|
13
14
|
fieldnames: str | list[str]
|
|
@@ -33,46 +34,24 @@ class TimeDependentSolver:
|
|
|
33
34
|
self.profiler = JAXMemoryProfiler()
|
|
34
35
|
else:
|
|
35
36
|
raise ValueError(f"Unsupported backend: {self.backend}")
|
|
36
|
-
|
|
37
|
-
def solve(
|
|
38
|
-
self,
|
|
39
|
-
time_increment=0.1,
|
|
40
|
-
frames=10,
|
|
41
|
-
max_iters=100,
|
|
42
|
-
problem_kwargs=None,
|
|
43
|
-
jit=True,
|
|
44
|
-
verbose=True,
|
|
45
|
-
vtk_out=False,
|
|
46
|
-
plot_bounds=None,
|
|
47
|
-
colormap='viridis'
|
|
48
|
-
):
|
|
49
|
-
"""Run the time integration loop.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
time_increment (float): Size of a single time step.
|
|
53
|
-
frames (int): Number of output frames (for plotting, vtk, checks).
|
|
54
|
-
max_iters (int): Number of time steps to compute.
|
|
55
|
-
problem_kwargs (dict | None): Problem-specific input arguments.
|
|
56
|
-
jit (bool): Create just-in-time compiled kernel if ``True``
|
|
57
|
-
verbose (bool | str): If ``True`` prints memory stats, ``'plot'``
|
|
58
|
-
updates an interactive plot.
|
|
59
|
-
vtk_out (bool): Write VTK files for each frame if ``True``.
|
|
60
|
-
plot_bounds (tuple | None): Optional value range for plots.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
problem_kwargs = problem_kwargs or {}
|
|
37
|
+
|
|
64
38
|
if isinstance(self.fieldnames, str):
|
|
65
39
|
self.fieldnames = [self.fieldnames]
|
|
66
40
|
else:
|
|
67
41
|
self.fieldnames = list(self.fieldnames)
|
|
68
42
|
|
|
43
|
+
def _init_fields(self):
|
|
44
|
+
"""Initialize fields in the voxel grid."""
|
|
69
45
|
u_list = [self.vg.init_scalar_field(self.vf.fields[name]) for name in self.fieldnames]
|
|
70
46
|
u = self.vg.concatenate(u_list, 0)
|
|
71
47
|
u = self.vg.bc.trim_boundary_nodes(u)
|
|
72
|
-
|
|
48
|
+
return u
|
|
49
|
+
|
|
50
|
+
def _init_stepper(self, time_increment, problem_kwargs, jit):
|
|
51
|
+
problem_kwargs = problem_kwargs or {}
|
|
73
52
|
if self.step_fn is not None:
|
|
74
|
-
step = self.step_fn
|
|
75
53
|
self.problem = None
|
|
54
|
+
step = self.step_fn
|
|
76
55
|
else:
|
|
77
56
|
if self.problem_cls is None or self.timestepper_cls is None:
|
|
78
57
|
raise ValueError("Either provide step_fn or both problem_cls and timestepper_cls")
|
|
@@ -88,23 +67,47 @@ class TimeDependentSolver:
|
|
|
88
67
|
import torch
|
|
89
68
|
step = torch.compile(step)
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
70
|
+
return step
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def _run_loop(self, u, step, time_increment, frames, max_iters,
|
|
74
|
+
vtk_out, verbose, plot_bounds, colormap):
|
|
75
|
+
"""Abstract method for running the time integration loop."""
|
|
76
|
+
raise NotImplementedError("Subclasses must implement _run_loop method.")
|
|
94
77
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
78
|
+
def solve(
|
|
79
|
+
self,
|
|
80
|
+
time_increment=0.1,
|
|
81
|
+
frames=10,
|
|
82
|
+
max_iters=100,
|
|
83
|
+
problem_kwargs=None,
|
|
84
|
+
jit=True,
|
|
85
|
+
verbose=True,
|
|
86
|
+
vtk_out=False,
|
|
87
|
+
plot_bounds=None,
|
|
88
|
+
colormap='viridis'
|
|
89
|
+
):
|
|
90
|
+
"""Run the time integration loop.
|
|
101
91
|
|
|
102
|
-
|
|
92
|
+
Args:
|
|
93
|
+
time_increment (float): Size of a single time step.
|
|
94
|
+
frames (int): Number of output frames (for plotting, vtk, checks).
|
|
95
|
+
max_iters (int): Number of time steps to compute.
|
|
96
|
+
problem_kwargs (dict | None): Problem-specific input arguments.
|
|
97
|
+
jit (bool): Create just-in-time compiled kernel if ``True``
|
|
98
|
+
verbose (bool | str): If ``True`` prints memory stats, ``'plot'``
|
|
99
|
+
updates an interactive plot.
|
|
100
|
+
vtk_out (bool): Write VTK files for each frame if ``True``.
|
|
101
|
+
plot_bounds (tuple | None): Optional value range for plots.
|
|
102
|
+
"""
|
|
103
|
+
u = self._init_fields()
|
|
104
|
+
step = self._init_stepper(time_increment, problem_kwargs, jit)
|
|
103
105
|
|
|
106
|
+
start = timer()
|
|
107
|
+
u = self._run_loop(u, step, time_increment, frames, max_iters,
|
|
108
|
+
vtk_out, verbose, plot_bounds, colormap)
|
|
104
109
|
end = timer()
|
|
105
|
-
|
|
106
|
-
self._handle_outputs(u, frame, time, slice_idx, vtk_out, verbose, plot_bounds, colormap)
|
|
107
|
-
|
|
110
|
+
self.computation_time = end - start
|
|
108
111
|
if verbose:
|
|
109
112
|
self.profiler.print_memory_stats(start, end, max_iters)
|
|
110
113
|
|
|
@@ -113,10 +116,10 @@ class TimeDependentSolver:
|
|
|
113
116
|
if getattr(self, 'problem', None) is not None:
|
|
114
117
|
u_out = self.vg.bc.trim_ghost_nodes(self.problem.pad_bc(u))
|
|
115
118
|
else:
|
|
116
|
-
u_out = u
|
|
119
|
+
u_out = self.vg.bc.trim_ghost_nodes(self.vg.pad_zeros(u))
|
|
117
120
|
|
|
118
121
|
for i, name in enumerate(self.fieldnames):
|
|
119
|
-
self.vf.
|
|
122
|
+
self.vf.set_field(name, self.vg.export_scalar_field_to_numpy(u_out[i:i+1]))
|
|
120
123
|
|
|
121
124
|
if verbose:
|
|
122
125
|
self.profiler.update_memory_stats()
|
|
@@ -133,3 +136,66 @@ class TimeDependentSolver:
|
|
|
133
136
|
if verbose == 'plot':
|
|
134
137
|
clear_output(wait=True)
|
|
135
138
|
self.vf.plot_slice(self.fieldnames[0], slice_idx, time=time, colormap=colormap, value_bounds=plot_bounds)
|
|
139
|
+
|
|
140
|
+
@dataclass
|
|
141
|
+
class TimeDependentSolver(BaseSolver):
|
|
142
|
+
"""Solver for time-dependent problems."""
|
|
143
|
+
def _run_loop(self, u, step, time_increment, frames, max_iters,
|
|
144
|
+
vtk_out, verbose, plot_bounds, colormap):
|
|
145
|
+
n_out = max_iters // frames
|
|
146
|
+
frame = 0
|
|
147
|
+
slice_idx = self.vf.Nz // 2
|
|
148
|
+
|
|
149
|
+
for i in range(max_iters):
|
|
150
|
+
time = i * time_increment
|
|
151
|
+
if i % n_out == 0:
|
|
152
|
+
self._handle_outputs(u, frame, time, slice_idx, vtk_out,
|
|
153
|
+
verbose, plot_bounds, colormap)
|
|
154
|
+
frame += 1
|
|
155
|
+
|
|
156
|
+
u = step(time, u)
|
|
157
|
+
time = max_iters * time_increment
|
|
158
|
+
self._handle_outputs(u, frame, time, slice_idx, vtk_out,
|
|
159
|
+
verbose, plot_bounds, colormap)
|
|
160
|
+
return u
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class SteadyStatePseudoTimeSolver(BaseSolver):
|
|
164
|
+
"""Solver for steady-state problems."""
|
|
165
|
+
conv_crit: float = 1e-6
|
|
166
|
+
check_freq: int = 10
|
|
167
|
+
|
|
168
|
+
def _run_loop(self, u, step, time_increment, frames, max_iters,
|
|
169
|
+
vtk_out, verbose, plot_bounds, colormap):
|
|
170
|
+
slice_idx = self.vf.Nz // 2
|
|
171
|
+
self.converged = False
|
|
172
|
+
self.iter = 0
|
|
173
|
+
|
|
174
|
+
while not self.converged and self.iter < max_iters:
|
|
175
|
+
time = self.iter * time_increment
|
|
176
|
+
diff = u - step(time, u)
|
|
177
|
+
u = step(time, u)
|
|
178
|
+
|
|
179
|
+
if self.iter % self.check_freq == 0:
|
|
180
|
+
self.converged = self.check_convergence(diff, verbose)
|
|
181
|
+
|
|
182
|
+
self._handle_outputs(u, 0, time, slice_idx, vtk_out,
|
|
183
|
+
verbose, plot_bounds, colormap)
|
|
184
|
+
return u
|
|
185
|
+
|
|
186
|
+
def check_convergence(self, diff, verbose):
|
|
187
|
+
"""Check for convergence based on relative change in fields."""
|
|
188
|
+
converged = True
|
|
189
|
+
for i, name in enumerate(self.fieldnames):
|
|
190
|
+
# Check if Frobenius norm of change is below threshold
|
|
191
|
+
rel_change = self.vg.lib.linalg.norm(diff[i]) / \
|
|
192
|
+
self.vg.lib.sqrt(self.vf.Nx * self.vf.Ny * self.vf.Nz)
|
|
193
|
+
if rel_change > self.conv_crit:
|
|
194
|
+
converged = False
|
|
195
|
+
if verbose:
|
|
196
|
+
print(f"Iter {self.iter}: Field '{name}' relative change: {rel_change:.2e}")
|
|
197
|
+
|
|
198
|
+
if converged and verbose:
|
|
199
|
+
print(f"Converged after {self.iter} iterations.")
|
|
200
|
+
|
|
201
|
+
return converged
|
|
File without changes
|
|
@@ -215,6 +215,8 @@ def mms_convergence_test(
|
|
|
215
215
|
vf = evo.VoxelFields((2**p, 2**p, 2**p), (1, 1, 1), convention=convention)
|
|
216
216
|
elif convention == 'staggered_x':
|
|
217
217
|
vf = evo.VoxelFields((2**p + 1, 2**p, 2**p), (1, 1, 1), convention=convention)
|
|
218
|
+
else:
|
|
219
|
+
raise ValueError("Chosen convention must be cell_center or staggered_x.")
|
|
218
220
|
vf.precision = dtype
|
|
219
221
|
dx[i] = vf.spacing[0]
|
|
220
222
|
|
|
@@ -231,6 +233,8 @@ def mms_convergence_test(
|
|
|
231
233
|
ODE = ODE_class(vg, **problem_kwargs)
|
|
232
234
|
rhs_orig = ODE.rhs
|
|
233
235
|
grid = vg.meshgrid()
|
|
236
|
+
if convention == 'staggered_x':
|
|
237
|
+
grid = (grid[0][1:-1,:,:], grid[1][1:-1,:,:], grid[2][1:-1,:,:])
|
|
234
238
|
|
|
235
239
|
# Construct new rhs including forcing term from MMS
|
|
236
240
|
if mode == 'temporal':
|
|
@@ -246,7 +250,6 @@ def mms_convergence_test(
|
|
|
246
250
|
u_ex_list.append(vg.expand_dim(u_list[j](t_, *grid), 0))
|
|
247
251
|
rhs = vg.set(rhs, j, rhs[j] + u_t_list[j](t_, *grid))
|
|
248
252
|
u_ex = vg.concatenate(u_ex_list, 0)
|
|
249
|
-
u_ex = vg.bc.trim_boundary_nodes(u_ex)
|
|
250
253
|
rhs -= rhs_orig(t, u_ex)
|
|
251
254
|
return rhs
|
|
252
255
|
|
|
@@ -322,7 +325,10 @@ def mms_convergence_test(
|
|
|
322
325
|
for j, func in enumerate(test_functions):
|
|
323
326
|
exact = vf.fields[f'u{j}_final']
|
|
324
327
|
diff = vf.fields[f'u{j}'] - exact
|
|
325
|
-
|
|
328
|
+
if convention == 'staggered_x':
|
|
329
|
+
errors[j, k, i] = np.linalg.norm(diff[1:-1,:,:]) / np.linalg.norm(exact[1:-1,:,:])
|
|
330
|
+
else:
|
|
331
|
+
errors[j, k, i] = np.linalg.norm(diff) / np.linalg.norm(exact)
|
|
326
332
|
|
|
327
333
|
# Fit slope after loop
|
|
328
334
|
def calc_slope(x, y):
|
|
@@ -120,30 +120,40 @@ class VoxelFields:
|
|
|
120
120
|
grid = Grid(self.shape, self.origin, self.spacing, self.convention)
|
|
121
121
|
return grid
|
|
122
122
|
|
|
123
|
-
def
|
|
123
|
+
def set_field(self, name: str, array: np.ndarray):
|
|
124
124
|
"""
|
|
125
|
-
|
|
125
|
+
Set field values for an existing field in the voxel grid.
|
|
126
126
|
|
|
127
127
|
Args:
|
|
128
128
|
name (str): Name of the field.
|
|
129
|
-
array (numpy.ndarray, optional): 3D array
|
|
129
|
+
array (numpy.ndarray, optional): 3D array.
|
|
130
130
|
|
|
131
131
|
Raises:
|
|
132
132
|
ValueError: If the provided array does not match the voxel grid dimensions.
|
|
133
133
|
TypeError: If the provided array is not a numpy array.
|
|
134
134
|
"""
|
|
135
|
-
if array
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
self.fields[name] = array
|
|
139
|
-
else:
|
|
140
|
-
raise ValueError(
|
|
141
|
-
f"The provided array must have the shape {self.shape}."
|
|
142
|
-
)
|
|
135
|
+
if isinstance(array, np.ndarray):
|
|
136
|
+
if array.shape == self.shape:
|
|
137
|
+
self.fields[name] = array
|
|
143
138
|
else:
|
|
144
|
-
raise
|
|
139
|
+
raise ValueError(
|
|
140
|
+
f"The provided array must have the shape {self.shape}."
|
|
141
|
+
)
|
|
142
|
+
else:
|
|
143
|
+
raise TypeError("The provided array must be a numpy array.")
|
|
144
|
+
|
|
145
|
+
def add_field(self, name: str, array=None):
|
|
146
|
+
"""
|
|
147
|
+
Adds a field to the voxel grid.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
name (str): Name of the field.
|
|
151
|
+
array (numpy.ndarray, optional): 3D array to initialize the field. If None, initializes with zeros.
|
|
152
|
+
"""
|
|
153
|
+
if array is not None:
|
|
154
|
+
self.set_field(name, array)
|
|
145
155
|
else:
|
|
146
|
-
self.
|
|
156
|
+
self.set_field(name, np.zeros(self.shape))
|
|
147
157
|
|
|
148
158
|
def set_voxel_sphere(self, name: str, center, radius, label: int | float = 1):
|
|
149
159
|
"""Create a voxelized representation of a sphere in 3D
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evoxels
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Differentiable physics framework for voxel-based microstructure simulations
|
|
5
5
|
Author-email: Simon Daubner <s.daubner@imperial.ac.uk>
|
|
6
6
|
License: MIT
|
|
@@ -40,6 +40,7 @@ Provides-Extra: notebooks
|
|
|
40
40
|
Requires-Dist: ipywidgets; extra == "notebooks"
|
|
41
41
|
Requires-Dist: ipympl; extra == "notebooks"
|
|
42
42
|
Requires-Dist: notebook; extra == "notebooks"
|
|
43
|
+
Requires-Dist: taufactor; extra == "notebooks"
|
|
43
44
|
Dynamic: license-file
|
|
44
45
|
|
|
45
46
|
[](https://github.com/daubners/evoxels/actions/workflows/python-package.yml)
|
|
@@ -81,7 +82,7 @@ TL;DR
|
|
|
81
82
|
```bash
|
|
82
83
|
conda create --name voxenv python=3.12
|
|
83
84
|
conda activate voxenv
|
|
84
|
-
pip install evoxels[torch,jax,dev,notebooks]
|
|
85
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
85
86
|
pip install --upgrade "jax[cuda12]"
|
|
86
87
|
```
|
|
87
88
|
|
|
@@ -103,7 +104,7 @@ Navigate to the evoxels folder, then
|
|
|
103
104
|
```
|
|
104
105
|
pip install -e .[torch] # install with torch backend
|
|
105
106
|
pip install -e .[jax] # install with jax backend
|
|
106
|
-
pip install -e .[dev,
|
|
107
|
+
pip install -e .[dev,notebooks] # install testing and notebooks
|
|
107
108
|
```
|
|
108
109
|
Note that the default `[jax]` installation is only CPU compatible. To install the corresponding CUDA libraries check your CUDA version with
|
|
109
110
|
```bash
|
|
@@ -115,7 +116,7 @@ pip install -U "jax[cuda12]"
|
|
|
115
116
|
```
|
|
116
117
|
To install both backends within one environment it is important to install torch first and then upgrade the `jax` installation e.g.
|
|
117
118
|
```bash
|
|
118
|
-
pip install evoxels[torch,
|
|
119
|
+
pip install "evoxels[torch,jax,dev,notebooks]"
|
|
119
120
|
pip install --upgrade "jax[cuda12]"
|
|
120
121
|
```
|
|
121
122
|
To work with the example notebooks install Jupyter and all notebook related dependencies via
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "evoxels"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "1.0.0"
|
|
8
8
|
description = "Differentiable physics framework for voxel-based microstructure simulations"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Simon Daubner", email = "s.daubner@imperial.ac.uk" }
|
|
@@ -53,7 +53,8 @@ dev = [
|
|
|
53
53
|
notebooks = [
|
|
54
54
|
"ipywidgets",
|
|
55
55
|
"ipympl",
|
|
56
|
-
"notebook"
|
|
56
|
+
"notebook",
|
|
57
|
+
"taufactor"
|
|
57
58
|
]
|
|
58
59
|
|
|
59
60
|
[tool.setuptools.packages.find]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|