Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be647658d3 | |||
| c46bed8298 | |||
| fb3bef23bf | |||
| d4f1008c5c | |||
| b486fa325b | |||
| b7ad5dea2b | |||
| 684b891e0f | |||
| 4a80ca8b12 | |||
| e3169b9e20 | |||
| 35ecbad15e | |||
| 43e01a814d | |||
| 9eb0e28bcb | |||
| c858b20d47 | |||
| 777ecbc024 | |||
| c4f8749941 | |||
| cd5cc9eb83 | |||
| 99e8d32eb1 | |||
| 1cb0cb2e4f | |||
| 234e8d7ac3 | |||
| 83f4d87ad8 | |||
| 1987ee473a | |||
| 4afc6cf62e | |||
| 53d5812b4a | |||
| 651e255704 | |||
| 71c2bbfada | |||
| 6a56921c12 | |||
| 006833acf2 | |||
| 155f30068f | |||
| 7987dc796f | |||
| 829007c672 | |||
| 659566750f | |||
| 76701f593c | |||
| 4e3a163522 | |||
| 50f92e1cc8 | |||
| b3c2fd391b | |||
| c543868c0b | |||
| e54735d9c6 | |||
| 4f2433320d | |||
| 47415a0beb | |||
| e459b5e61f | |||
| 36431cd0e4 | |||
| 739e96df3d | |||
| 63e7cb949f | |||
| c53a3c4d84 | |||
| 5dd9994e76 | |||
| 1021768e30 | |||
| 95e923d7b7 | |||
| 3f8802cb5f | |||
| 43bb0ba379 | |||
| e19968bb9f | |||
| 43f038d761 | |||
| d5fca741d1 | |||
| ca94ad1b25 | |||
| 10f26c12b4 | |||
| ee51c7db49 | |||
| 36bea6a593 | |||
| b16b35d84a | |||
| 6f3ae5a64f | |||
| 99c22d572f | |||
| 2f00baf0c6 | |||
| 2712d96f2a | |||
| dc3e733e7f | |||
| 95e3f71b40 | |||
| 639f88bba8 |
103
examples/fdfd0.py
Normal file
103
examples/fdfd0.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import numpy
|
||||||
|
from numpy.linalg import norm
|
||||||
|
from matplotlib import pyplot, colors
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import meanas
|
||||||
|
from meanas import fdtd
|
||||||
|
from meanas.fdmath import vec, unvec
|
||||||
|
from meanas.fdfd import waveguide_3d, functional, scpml, operators
|
||||||
|
from meanas.fdfd.solvers import generic as generic_solver
|
||||||
|
|
||||||
|
import gridlock
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
logging.getLogger('matplotlib').setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
|
def pcolor(ax, v) -> None:
|
||||||
|
mappable = ax.pcolor(v, cmap='seismic', norm=colors.CenteredNorm())
|
||||||
|
ax.axis('equal')
|
||||||
|
ax.get_figure().colorbar(mappable)
|
||||||
|
|
||||||
|
|
||||||
|
def test0(solver=generic_solver):
|
||||||
|
dx = 50 # discretization (nm/cell)
|
||||||
|
pml_thickness = 10 # (number of cells)
|
||||||
|
|
||||||
|
wl = 1550 # Excitation wavelength
|
||||||
|
omega = 2 * numpy.pi / wl
|
||||||
|
|
||||||
|
# Device design parameters
|
||||||
|
radii = (1, 0.6)
|
||||||
|
th = 220
|
||||||
|
center = [0, 0, 0]
|
||||||
|
|
||||||
|
# refractive indices
|
||||||
|
n_ring = numpy.sqrt(12.6) # ~Si
|
||||||
|
n_air = 4.0 # air
|
||||||
|
|
||||||
|
# Half-dimensions of the simulation grid
|
||||||
|
xyz_max = numpy.array([1.2, 1.2, 0.3]) * 1000 + pml_thickness * dx
|
||||||
|
|
||||||
|
# Coordinates of the edges of the cells.
|
||||||
|
half_edge_coords = [numpy.arange(dx/2, m + dx, step=dx) for m in xyz_max]
|
||||||
|
edge_coords = [numpy.hstack((-h[::-1], h)) for h in half_edge_coords]
|
||||||
|
|
||||||
|
# #### Create the grid, mask, and draw the device ####
|
||||||
|
grid = gridlock.Grid(edge_coords)
|
||||||
|
epsilon = grid.allocate(n_air**2, dtype=numpy.float32)
|
||||||
|
grid.draw_cylinder(
|
||||||
|
epsilon,
|
||||||
|
h = dict(axis='z', center=center[2], span=th),
|
||||||
|
radius = max(radii),
|
||||||
|
center2d = center[:2],
|
||||||
|
foreground = n_ring ** 2,
|
||||||
|
num_points = 24,
|
||||||
|
)
|
||||||
|
grid.draw_cylinder(
|
||||||
|
epsilon,
|
||||||
|
h = dict(axis='z', center=center[2], span=th * 1.1),
|
||||||
|
radius = min(radii),
|
||||||
|
center2d = center[:2],
|
||||||
|
foreground = n_air ** 2,
|
||||||
|
num_points = 24,
|
||||||
|
)
|
||||||
|
|
||||||
|
dxes = [grid.dxyz, grid.autoshifted_dxyz()]
|
||||||
|
for a in (0, 1, 2):
|
||||||
|
for p in (-1, 1):
|
||||||
|
dxes = meanas.fdfd.scpml.stretch_with_scpml(dxes, axis=a, polarity=p, omega=omega,
|
||||||
|
thickness=pml_thickness)
|
||||||
|
|
||||||
|
J = [numpy.zeros_like(epsilon[0], dtype=complex) for _ in range(3)]
|
||||||
|
J[1][15, grid.shape[1]//2, grid.shape[2]//2] = 1
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Solve!
|
||||||
|
#
|
||||||
|
sim_args = dict(
|
||||||
|
omega = omega,
|
||||||
|
dxes = dxes,
|
||||||
|
epsilon = vec(epsilon),
|
||||||
|
)
|
||||||
|
x = solver(J=vec(J), **sim_args)
|
||||||
|
|
||||||
|
A = operators.e_full(omega, dxes, vec(epsilon)).tocsr()
|
||||||
|
b = -1j * omega * vec(J)
|
||||||
|
print('Norm of the residual is ', norm(A @ x - b) / norm(b))
|
||||||
|
|
||||||
|
E = unvec(x, grid.shape)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Plot results
|
||||||
|
#
|
||||||
|
grid.visualize_slice(E.real, plane=dict(z=0), which_shifts=1, pcolormesh_args=dict(norm=colors.CenteredNorm(), cmap='bwr'))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test0()
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
|
from matplotlib import pyplot, colors
|
||||||
|
import logging
|
||||||
|
|
||||||
import meanas
|
import meanas
|
||||||
from meanas import fdtd
|
from meanas import fdtd
|
||||||
@ -10,9 +12,6 @@ from meanas.fdfd.solvers import generic as generic_solver
|
|||||||
|
|
||||||
import gridlock
|
import gridlock
|
||||||
|
|
||||||
from matplotlib import pyplot
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logging.getLogger('matplotlib').setLevel(logging.WARNING)
|
logging.getLogger('matplotlib').setLevel(logging.WARNING)
|
||||||
@ -20,82 +19,6 @@ logging.getLogger('matplotlib').setLevel(logging.WARNING)
|
|||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
def test0(solver=generic_solver):
|
|
||||||
dx = 50 # discretization (nm/cell)
|
|
||||||
pml_thickness = 10 # (number of cells)
|
|
||||||
|
|
||||||
wl = 1550 # Excitation wavelength
|
|
||||||
omega = 2 * numpy.pi / wl
|
|
||||||
|
|
||||||
# Device design parameters
|
|
||||||
radii = (1, 0.6)
|
|
||||||
th = 220
|
|
||||||
center = [0, 0, 0]
|
|
||||||
|
|
||||||
# refractive indices
|
|
||||||
n_ring = numpy.sqrt(12.6) # ~Si
|
|
||||||
n_air = 4.0 # air
|
|
||||||
|
|
||||||
# Half-dimensions of the simulation grid
|
|
||||||
xyz_max = numpy.array([1.2, 1.2, 0.3]) * 1000 + pml_thickness * dx
|
|
||||||
|
|
||||||
# Coordinates of the edges of the cells.
|
|
||||||
half_edge_coords = [numpy.arange(dx/2, m + dx, step=dx) for m in xyz_max]
|
|
||||||
edge_coords = [numpy.hstack((-h[::-1], h)) for h in half_edge_coords]
|
|
||||||
|
|
||||||
# #### Create the grid, mask, and draw the device ####
|
|
||||||
grid = gridlock.Grid(edge_coords)
|
|
||||||
epsilon = grid.allocate(n_air**2, dtype=numpy.float32)
|
|
||||||
grid.draw_cylinder(epsilon,
|
|
||||||
surface_normal=2,
|
|
||||||
center=center,
|
|
||||||
radius=max(radii),
|
|
||||||
thickness=th,
|
|
||||||
eps=n_ring**2,
|
|
||||||
num_points=24)
|
|
||||||
grid.draw_cylinder(epsilon,
|
|
||||||
surface_normal=2,
|
|
||||||
center=center,
|
|
||||||
radius=min(radii),
|
|
||||||
thickness=th*1.1,
|
|
||||||
eps=n_air ** 2,
|
|
||||||
num_points=24)
|
|
||||||
|
|
||||||
dxes = [grid.dxyz, grid.autoshifted_dxyz()]
|
|
||||||
for a in (0, 1, 2):
|
|
||||||
for p in (-1, 1):
|
|
||||||
dxes = meanas.fdfd.scpml.stretch_with_scpml(dxes, axis=a, polarity=p, omega=omega,
|
|
||||||
thickness=pml_thickness)
|
|
||||||
|
|
||||||
J = [numpy.zeros_like(epsilon[0], dtype=complex) for _ in range(3)]
|
|
||||||
J[1][15, grid.shape[1]//2, grid.shape[2]//2] = 1
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
Solve!
|
|
||||||
'''
|
|
||||||
sim_args = {
|
|
||||||
'omega': omega,
|
|
||||||
'dxes': dxes,
|
|
||||||
'epsilon': vec(epsilon),
|
|
||||||
}
|
|
||||||
x = solver(J=vec(J), **sim_args)
|
|
||||||
|
|
||||||
A = operators.e_full(omega, dxes, vec(epsilon)).tocsr()
|
|
||||||
b = -1j * omega * vec(J)
|
|
||||||
print('Norm of the residual is ', norm(A @ x - b))
|
|
||||||
|
|
||||||
E = unvec(x, grid.shape)
|
|
||||||
|
|
||||||
'''
|
|
||||||
Plot results
|
|
||||||
'''
|
|
||||||
pyplot.figure()
|
|
||||||
pyplot.pcolor(numpy.real(E[1][:, :, grid.shape[2]//2]), cmap='seismic')
|
|
||||||
pyplot.axis('equal')
|
|
||||||
pyplot.show()
|
|
||||||
|
|
||||||
|
|
||||||
def test1(solver=generic_solver):
|
def test1(solver=generic_solver):
|
||||||
dx = 40 # discretization (nm/cell)
|
dx = 40 # discretization (nm/cell)
|
||||||
pml_thickness = 10 # (number of cells)
|
pml_thickness = 10 # (number of cells)
|
||||||
@ -122,7 +45,7 @@ def test1(solver=generic_solver):
|
|||||||
# #### Create the grid and draw the device ####
|
# #### Create the grid and draw the device ####
|
||||||
grid = gridlock.Grid(edge_coords)
|
grid = gridlock.Grid(edge_coords)
|
||||||
epsilon = grid.allocate(n_air**2, dtype=numpy.float32)
|
epsilon = grid.allocate(n_air**2, dtype=numpy.float32)
|
||||||
grid.draw_cuboid(epsilon, center=center, dimensions=[8e3, w, th], eps=n_wg**2)
|
grid.draw_cuboid(epsilon, x=dict(center=0, span=8e3), y=dict(center=0, span=w), z=dict(center=0, span=th), foreground=n_wg**2)
|
||||||
|
|
||||||
dxes = [grid.dxyz, grid.autoshifted_dxyz()]
|
dxes = [grid.dxyz, grid.autoshifted_dxyz()]
|
||||||
for a in (0, 1, 2):
|
for a in (0, 1, 2):
|
||||||
@ -156,22 +79,14 @@ def test1(solver=generic_solver):
|
|||||||
# grid.draw_cuboid(pmcg, center=[700, 0, 0], dimensions=[80, 1e8, 1e8], eps=1)
|
# grid.draw_cuboid(pmcg, center=[700, 0, 0], dimensions=[80, 1e8, 1e8], eps=1)
|
||||||
# grid.visualize_isosurface(pmcg)
|
# grid.visualize_isosurface(pmcg)
|
||||||
|
|
||||||
def pcolor(v) -> None:
|
grid.visualize_slice(J.imag, plane=dict(y=6*dx), which_shifts=1, pcolormesh_args=dict(norm=colors.CenteredNorm(), cmap='bwr'))
|
||||||
vmax = numpy.max(numpy.abs(v))
|
fig, ax = pyplot.subplots()
|
||||||
pyplot.pcolor(v, cmap='seismic', vmin=-vmax, vmax=vmax)
|
ax.pcolormesh((numpy.abs(J).sum(axis=2).sum(axis=0) > 0).astype(float).T, cmap='hot')
|
||||||
pyplot.axis('equal')
|
|
||||||
pyplot.colorbar()
|
|
||||||
|
|
||||||
ss = (1, slice(None), J.shape[2]//2+6, slice(None))
|
|
||||||
# pyplot.figure()
|
|
||||||
# pcolor(J3[ss].T.imag)
|
|
||||||
# pyplot.figure()
|
|
||||||
# pcolor((numpy.abs(J3).sum(axis=2).sum(axis=0) > 0).astype(float).T)
|
|
||||||
pyplot.show(block=True)
|
pyplot.show(block=True)
|
||||||
|
|
||||||
'''
|
#
|
||||||
Solve!
|
# Solve!
|
||||||
'''
|
#
|
||||||
sim_args = {
|
sim_args = {
|
||||||
'omega': omega,
|
'omega': omega,
|
||||||
'dxes': dxes,
|
'dxes': dxes,
|
||||||
@ -188,20 +103,18 @@ def test1(solver=generic_solver):
|
|||||||
|
|
||||||
E = unvec(x, grid.shape)
|
E = unvec(x, grid.shape)
|
||||||
|
|
||||||
'''
|
#
|
||||||
Plot results
|
# Plot results
|
||||||
'''
|
#
|
||||||
center = grid.pos2ind([0, 0, 0], None).astype(int)
|
center = grid.pos2ind([0, 0, 0], None).astype(int)
|
||||||
pyplot.figure()
|
fig, axes = pyplot.subplots(2, 2)
|
||||||
pyplot.subplot(2, 2, 1)
|
grid.visualize_slice(E.real, plane=dict(x=0), which_shifts=1, ax=axes[0, 0], finalize=False, pcolormesh_args=dict(norm=colors.CenteredNorm(), cmap='bwr'))
|
||||||
pcolor(numpy.real(E[1][center[0], :, :]).T)
|
grid.visualize_slice(E.real, plane=dict(z=0), which_shifts=1, ax=axes[0, 1], finalize=False, pcolormesh_args=dict(norm=colors.CenteredNorm(), cmap='bwr'))
|
||||||
pyplot.subplot(2, 2, 2)
|
# pcolor(axes[0, 0], numpy.real(E[1][center[0], :, :]).T)
|
||||||
pyplot.plot(numpy.log10(numpy.abs(E[1][:, center[1], center[2]]) + 1e-10))
|
# pcolor(axes[0, 1], numpy.real(E[1][:, :, center[2]]).T)
|
||||||
pyplot.grid(alpha=0.6)
|
axes[1, 0].plot(numpy.log10(numpy.abs(E[1][:, center[1], center[2]]) + 1e-10))
|
||||||
pyplot.ylabel('log10 of field')
|
axes[1, 0].grid(alpha=0.6)
|
||||||
pyplot.subplot(2, 2, 3)
|
axes[1, 0].set_ylabel('log10 of field')
|
||||||
pcolor(numpy.real(E[1][:, :, center[2]]).T)
|
|
||||||
pyplot.subplot(2, 2, 4)
|
|
||||||
|
|
||||||
def poyntings(E):
|
def poyntings(E):
|
||||||
H = functional.e2h(omega, dxes)(E)
|
H = functional.e2h(omega, dxes)(E)
|
||||||
@ -215,24 +128,28 @@ def test1(solver=generic_solver):
|
|||||||
return s0, s1, s2
|
return s0, s1, s2
|
||||||
|
|
||||||
s0x, s1x, s2x = poyntings(E)
|
s0x, s1x, s2x = poyntings(E)
|
||||||
pyplot.plot(s0x[0].sum(axis=2).sum(axis=1), label='s0', marker='.')
|
ax = axes[1, 1]
|
||||||
pyplot.plot(s1x[0].sum(axis=2).sum(axis=1), label='s1', marker='.')
|
ax.plot(s0x[0].sum(axis=2).sum(axis=1), label='s0', marker='.')
|
||||||
pyplot.plot(s2x[0].sum(axis=2).sum(axis=1), label='s2', marker='.')
|
ax.plot(s1x[0].sum(axis=2).sum(axis=1), label='s1', marker='.')
|
||||||
pyplot.plot(E[1][:, center[1], center[2]].real.T, label='Ey', marker='x')
|
ax.plot(s2x[0].sum(axis=2).sum(axis=1), label='s2', marker='.')
|
||||||
pyplot.grid(alpha=0.6)
|
ax.plot(E[1][:, center[1], center[2]].real.T, label='Ey', marker='x')
|
||||||
pyplot.legend()
|
ax.grid(alpha=0.6)
|
||||||
pyplot.show()
|
ax.legend()
|
||||||
|
|
||||||
|
p_in = (-E * J.conj()).sum() / 2 * (dx * dx * dx)
|
||||||
|
print(f'{p_in=}')
|
||||||
|
|
||||||
q = []
|
q = []
|
||||||
for i in range(-5, 30):
|
for i in range(-5, 30):
|
||||||
e_ovl_rolled = numpy.roll(e_overlap, i, axis=1)
|
e_ovl_rolled = numpy.roll(e_overlap, i, axis=1)
|
||||||
q += [numpy.abs(vec(E) @ vec(e_ovl_rolled).conj())]
|
q += [numpy.abs(vec(E).conj() @ vec(e_ovl_rolled))]
|
||||||
pyplot.figure()
|
fig, ax = pyplot.subplots()
|
||||||
pyplot.plot(q, marker='.')
|
ax.plot(q, marker='.')
|
||||||
pyplot.grid(alpha=0.6)
|
ax.grid(alpha=0.6)
|
||||||
pyplot.title('Overlap with mode')
|
ax.set_title('Overlap with mode')
|
||||||
pyplot.show()
|
print('Average overlap with mode:', sum(q[8:32])/len(q[8:32]))
|
||||||
print('Average overlap with mode:', sum(q)/len(q))
|
|
||||||
|
pyplot.show(block=True)
|
||||||
|
|
||||||
|
|
||||||
def module_available(name):
|
def module_available(name):
|
||||||
@ -240,9 +157,6 @@ def module_available(name):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#test0()
|
|
||||||
# test1()
|
|
||||||
|
|
||||||
if module_available('opencl_fdfd'):
|
if module_available('opencl_fdfd'):
|
||||||
from opencl_fdfd import cg_solver as opencl_solver
|
from opencl_fdfd import cg_solver as opencl_solver
|
||||||
test1(opencl_solver)
|
test1(opencl_solver)
|
||||||
@ -253,3 +167,4 @@ if __name__ == '__main__':
|
|||||||
# test1(magma_solver)
|
# test1(magma_solver)
|
||||||
else:
|
else:
|
||||||
test1()
|
test1()
|
||||||
|
|
||||||
@ -6,12 +6,13 @@ See the readme or `import meanas; help(meanas)` for more info.
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
__version__ = '0.9'
|
__version__ = '0.10'
|
||||||
__author__ = 'Jan Petykiewicz'
|
__author__ = 'Jan Petykiewicz'
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(pathlib.Path(__file__).parent / 'README.md', 'r') as f:
|
readme_path = pathlib.Path(__file__).parent / 'README.md'
|
||||||
|
with readme_path.open('r') as f:
|
||||||
__doc__ = f.read()
|
__doc__ = f.read()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
"""
|
"""
|
||||||
Solvers for eigenvalue / eigenvector problems
|
Solvers for eigenvalue / eigenvector problems
|
||||||
"""
|
"""
|
||||||
from typing import Callable
|
from collections.abc import Callable
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray, ArrayLike
|
from numpy.typing import NDArray, ArrayLike
|
||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
from scipy import sparse # type: ignore
|
from scipy import sparse
|
||||||
import scipy.sparse.linalg as spalg # type: ignore
|
import scipy.sparse.linalg as spalg
|
||||||
|
|
||||||
|
|
||||||
def power_iteration(
|
def power_iteration(
|
||||||
@ -25,8 +25,9 @@ def power_iteration(
|
|||||||
Returns:
|
Returns:
|
||||||
(Largest-magnitude eigenvalue, Corresponding eigenvector estimate)
|
(Largest-magnitude eigenvalue, Corresponding eigenvector estimate)
|
||||||
"""
|
"""
|
||||||
|
rng = numpy.random.default_rng()
|
||||||
if guess_vector is None:
|
if guess_vector is None:
|
||||||
v = numpy.random.rand(operator.shape[0]) + 1j * numpy.random.rand(operator.shape[0])
|
v = rng.random(operator.shape[0]) + 1j * rng.random(operator.shape[0])
|
||||||
else:
|
else:
|
||||||
v = guess_vector
|
v = guess_vector
|
||||||
|
|
||||||
@ -63,10 +64,10 @@ def rayleigh_quotient_iteration(
|
|||||||
(eigenvalues, eigenvectors)
|
(eigenvalues, eigenvectors)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
(operator - sparse.eye(operator.shape[0]))
|
(operator - sparse.eye_array(operator.shape[0]))
|
||||||
|
|
||||||
def shift(eigval: float) -> sparse:
|
def shift(eigval: float) -> sparse.sparray:
|
||||||
return eigval * sparse.eye(operator.shape[0])
|
return eigval * sparse.eye_array(operator.shape[0])
|
||||||
|
|
||||||
if solver is None:
|
if solver is None:
|
||||||
solver = spalg.spsolve
|
solver = spalg.spsolve
|
||||||
@ -129,12 +130,12 @@ def signed_eigensolve(
|
|||||||
|
|
||||||
# Try to combine, use general LinearOperator if we fail
|
# Try to combine, use general LinearOperator if we fail
|
||||||
try:
|
try:
|
||||||
shifted_operator = operator + shift * sparse.eye(operator.shape[0])
|
shifted_operator = operator + shift * sparse.eye_array(operator.shape[0])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
shifted_operator = operator + spalg.LinearOperator(shape=operator.shape,
|
shifted_operator = operator + spalg.LinearOperator(shape=operator.shape,
|
||||||
matvec=lambda v: shift * v)
|
matvec=lambda v: shift * v)
|
||||||
|
|
||||||
shifted_eigenvalues, eigenvectors = spalg.eigs(shifted_operator, which='LM', k=how_many, ncv=50)
|
shifted_eigenvalues, eigenvectors = spalg.eigs(shifted_operator, which='LM', k=how_many, ncv=2 * how_many + 50)
|
||||||
eigenvalues = shifted_eigenvalues - shift
|
eigenvalues = shifted_eigenvalues - shift
|
||||||
|
|
||||||
k = eigenvalues.argsort()
|
k = eigenvalues.argsort()
|
||||||
|
|||||||
@ -91,5 +91,12 @@ $$
|
|||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from . import solvers, operators, functional, scpml, waveguide_2d, waveguide_3d
|
from . import (
|
||||||
|
solvers as solvers,
|
||||||
|
operators as operators,
|
||||||
|
functional as functional,
|
||||||
|
scpml as scpml,
|
||||||
|
waveguide_2d as waveguide_2d,
|
||||||
|
waveguide_3d as waveguide_3d,
|
||||||
|
)
|
||||||
# from . import farfield, bloch TODO
|
# from . import farfield, bloch TODO
|
||||||
|
|||||||
@ -94,18 +94,19 @@ This module contains functions for generating and solving the
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Callable, Any, cast, Sequence
|
from typing import Any, cast
|
||||||
|
from collections.abc import Callable, Sequence
|
||||||
import logging
|
import logging
|
||||||
import numpy
|
import numpy
|
||||||
from numpy import pi, real, trace
|
from numpy import pi, real, trace
|
||||||
from numpy.fft import fftfreq
|
from numpy.fft import fftfreq
|
||||||
from numpy.typing import NDArray, ArrayLike
|
from numpy.typing import NDArray, ArrayLike
|
||||||
import scipy # type: ignore
|
import scipy
|
||||||
import scipy.optimize # type: ignore
|
import scipy.optimize
|
||||||
from scipy.linalg import norm # type: ignore
|
from scipy.linalg import norm
|
||||||
import scipy.sparse.linalg as spalg # type: ignore
|
import scipy.sparse.linalg as spalg
|
||||||
|
|
||||||
from ..fdmath import fdfield_t, cfdfield_t
|
from ..fdmath import fdfield, cfdfield, cfdfield_t
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -114,7 +115,6 @@ logger = logging.getLogger(__name__)
|
|||||||
try:
|
try:
|
||||||
import pyfftw.interfaces.numpy_fft # type: ignore
|
import pyfftw.interfaces.numpy_fft # type: ignore
|
||||||
import pyfftw.interfaces # type: ignore
|
import pyfftw.interfaces # type: ignore
|
||||||
import multiprocessing
|
|
||||||
logger.info('Using pyfftw')
|
logger.info('Using pyfftw')
|
||||||
|
|
||||||
pyfftw.interfaces.cache.enable()
|
pyfftw.interfaces.cache.enable()
|
||||||
@ -155,7 +155,7 @@ def generate_kmn(
|
|||||||
All are given in the xyz basis (e.g. `|k|[0,0,0] = norm(G_matrix @ k0)`).
|
All are given in the xyz basis (e.g. `|k|[0,0,0] = norm(G_matrix @ k0)`).
|
||||||
"""
|
"""
|
||||||
k0 = numpy.array(k0)
|
k0 = numpy.array(k0)
|
||||||
G_matrix = numpy.array(G_matrix, copy=False)
|
G_matrix = numpy.asarray(G_matrix)
|
||||||
|
|
||||||
Gi_grids = numpy.array(numpy.meshgrid(*(fftfreq(n, 1 / n) for n in shape[:3]), indexing='ij'))
|
Gi_grids = numpy.array(numpy.meshgrid(*(fftfreq(n, 1 / n) for n in shape[:3]), indexing='ij'))
|
||||||
Gi = numpy.moveaxis(Gi_grids, 0, -1)
|
Gi = numpy.moveaxis(Gi_grids, 0, -1)
|
||||||
@ -183,8 +183,8 @@ def generate_kmn(
|
|||||||
def maxwell_operator(
|
def maxwell_operator(
|
||||||
k0: ArrayLike,
|
k0: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None
|
mu: fdfield | None = None
|
||||||
) -> Callable[[NDArray[numpy.complex128]], NDArray[numpy.complex128]]:
|
) -> Callable[[NDArray[numpy.complex128]], NDArray[numpy.complex128]]:
|
||||||
"""
|
"""
|
||||||
Generate the Maxwell operator
|
Generate the Maxwell operator
|
||||||
@ -232,13 +232,13 @@ def maxwell_operator(
|
|||||||
Raveled conv(1/mu_k, ik x conv(1/eps_k, ik x h_mn)), returned
|
Raveled conv(1/mu_k, ik x conv(1/eps_k, ik x h_mn)), returned
|
||||||
and overwritten in-place of `h`.
|
and overwritten in-place of `h`.
|
||||||
"""
|
"""
|
||||||
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
hin_m, hin_n = (hi.reshape(shape) for hi in numpy.split(h, 2))
|
||||||
|
|
||||||
#{d,e,h}_xyz fields are complex 3-fields in (1/x, 1/y, 1/z) basis
|
#{d,e,h}_xyz fields are complex 3-fields in (1/x, 1/y, 1/z) basis
|
||||||
|
|
||||||
# cross product and transform into xyz basis
|
# cross product and transform into xyz basis
|
||||||
d_xyz = (n * hin_m
|
d_xyz = (n * hin_m
|
||||||
- m * hin_n) * k_mag # noqa: E128
|
- m * hin_n) * k_mag
|
||||||
|
|
||||||
# divide by epsilon
|
# divide by epsilon
|
||||||
temp = ifftn(d_xyz, axes=range(3)) # reuses d_xyz if using pyfftw
|
temp = ifftn(d_xyz, axes=range(3)) # reuses d_xyz if using pyfftw
|
||||||
@ -254,7 +254,7 @@ def maxwell_operator(
|
|||||||
else:
|
else:
|
||||||
# transform from mn to xyz
|
# transform from mn to xyz
|
||||||
b_xyz = (m * b_m[:, :, :, None]
|
b_xyz = (m * b_m[:, :, :, None]
|
||||||
+ n * b_n[:, :, :, None]) # noqa: E128
|
+ n * b_n[:, :, :, None])
|
||||||
|
|
||||||
# divide by mu
|
# divide by mu
|
||||||
temp = ifftn(b_xyz, axes=range(3))
|
temp = ifftn(b_xyz, axes=range(3))
|
||||||
@ -276,7 +276,7 @@ def maxwell_operator(
|
|||||||
def hmn_2_exyz(
|
def hmn_2_exyz(
|
||||||
k0: ArrayLike,
|
k0: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
) -> Callable[[NDArray[numpy.complex128]], cfdfield_t]:
|
) -> Callable[[NDArray[numpy.complex128]], cfdfield_t]:
|
||||||
"""
|
"""
|
||||||
Generate an operator which converts a vectorized spatial-frequency-space
|
Generate an operator which converts a vectorized spatial-frequency-space
|
||||||
@ -303,12 +303,13 @@ def hmn_2_exyz(
|
|||||||
k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
||||||
|
|
||||||
def operator(h: NDArray[numpy.complex128]) -> cfdfield_t:
|
def operator(h: NDArray[numpy.complex128]) -> cfdfield_t:
|
||||||
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
hin_m, hin_n = (hi.reshape(shape) for hi in numpy.split(h, 2))
|
||||||
d_xyz = (n * hin_m
|
d_xyz = (n * hin_m
|
||||||
- m * hin_n) * k_mag # noqa: E128
|
- m * hin_n) * k_mag
|
||||||
|
|
||||||
# divide by epsilon
|
# divide by epsilon
|
||||||
return numpy.array([ei for ei in numpy.moveaxis(ifftn(d_xyz, axes=range(3)) / epsilon, 3, 0)]) # TODO avoid copy
|
exyz = numpy.moveaxis(ifftn(d_xyz, axes=range(3)) / epsilon, 3, 0)
|
||||||
|
return cfdfield_t(exyz)
|
||||||
|
|
||||||
return operator
|
return operator
|
||||||
|
|
||||||
@ -316,7 +317,7 @@ def hmn_2_exyz(
|
|||||||
def hmn_2_hxyz(
|
def hmn_2_hxyz(
|
||||||
k0: ArrayLike,
|
k0: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t
|
epsilon: fdfield,
|
||||||
) -> Callable[[NDArray[numpy.complex128]], cfdfield_t]:
|
) -> Callable[[NDArray[numpy.complex128]], cfdfield_t]:
|
||||||
"""
|
"""
|
||||||
Generate an operator which converts a vectorized spatial-frequency-space
|
Generate an operator which converts a vectorized spatial-frequency-space
|
||||||
@ -341,10 +342,10 @@ def hmn_2_hxyz(
|
|||||||
_k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
_k_mag, m, n = generate_kmn(k0, G_matrix, shape)
|
||||||
|
|
||||||
def operator(h: NDArray[numpy.complex128]) -> cfdfield_t:
|
def operator(h: NDArray[numpy.complex128]) -> cfdfield_t:
|
||||||
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
hin_m, hin_n = (hi.reshape(shape) for hi in numpy.split(h, 2))
|
||||||
h_xyz = (m * hin_m
|
h_xyz = (m * hin_m
|
||||||
+ n * hin_n) # noqa: E128
|
+ n * hin_n)
|
||||||
return numpy.array([ifftn(hi) for hi in numpy.moveaxis(h_xyz, 3, 0)])
|
return cfdfield_t(numpy.array([ifftn(hi) for hi in numpy.moveaxis(h_xyz, 3, 0)]))
|
||||||
|
|
||||||
return operator
|
return operator
|
||||||
|
|
||||||
@ -352,8 +353,8 @@ def hmn_2_hxyz(
|
|||||||
def inverse_maxwell_operator_approx(
|
def inverse_maxwell_operator_approx(
|
||||||
k0: ArrayLike,
|
k0: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> Callable[[NDArray[numpy.complex128]], NDArray[numpy.complex128]]:
|
) -> Callable[[NDArray[numpy.complex128]], NDArray[numpy.complex128]]:
|
||||||
"""
|
"""
|
||||||
Generate an approximate inverse of the Maxwell operator,
|
Generate an approximate inverse of the Maxwell operator,
|
||||||
@ -394,7 +395,7 @@ def inverse_maxwell_operator_approx(
|
|||||||
Returns:
|
Returns:
|
||||||
Raveled ik x conv(eps_k, ik x conv(mu_k, h_mn))
|
Raveled ik x conv(eps_k, ik x conv(mu_k, h_mn))
|
||||||
"""
|
"""
|
||||||
hin_m, hin_n = [hi.reshape(shape) for hi in numpy.split(h, 2)]
|
hin_m, hin_n = (hi.reshape(shape) for hi in numpy.split(h, 2))
|
||||||
|
|
||||||
#{d,e,h}_xyz fields are complex 3-fields in (1/x, 1/y, 1/z) basis
|
#{d,e,h}_xyz fields are complex 3-fields in (1/x, 1/y, 1/z) basis
|
||||||
|
|
||||||
@ -403,7 +404,7 @@ def inverse_maxwell_operator_approx(
|
|||||||
else:
|
else:
|
||||||
# transform from mn to xyz
|
# transform from mn to xyz
|
||||||
h_xyz = (m * hin_m[:, :, :, None]
|
h_xyz = (m * hin_m[:, :, :, None]
|
||||||
+ n * hin_n[:, :, :, None]) # noqa: E128
|
+ n * hin_n[:, :, :, None])
|
||||||
|
|
||||||
# multiply by mu
|
# multiply by mu
|
||||||
temp = ifftn(h_xyz, axes=range(3))
|
temp = ifftn(h_xyz, axes=range(3))
|
||||||
@ -416,7 +417,7 @@ def inverse_maxwell_operator_approx(
|
|||||||
|
|
||||||
# cross product and transform into xyz basis
|
# cross product and transform into xyz basis
|
||||||
e_xyz = (n * b_m
|
e_xyz = (n * b_m
|
||||||
- m * b_n) / k_mag # noqa: E128
|
- m * b_n) / k_mag
|
||||||
|
|
||||||
# multiply by epsilon
|
# multiply by epsilon
|
||||||
temp = ifftn(e_xyz, axes=range(3))
|
temp = ifftn(e_xyz, axes=range(3))
|
||||||
@ -440,8 +441,8 @@ def find_k(
|
|||||||
tolerance: float,
|
tolerance: float,
|
||||||
direction: ArrayLike,
|
direction: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
band: int = 0,
|
band: int = 0,
|
||||||
k_bounds: tuple[float, float] = (0, 0.5),
|
k_bounds: tuple[float, float] = (0, 0.5),
|
||||||
k_guess: float | None = None,
|
k_guess: float | None = None,
|
||||||
@ -508,8 +509,8 @@ def eigsolve(
|
|||||||
num_modes: int,
|
num_modes: int,
|
||||||
k0: ArrayLike,
|
k0: ArrayLike,
|
||||||
G_matrix: ArrayLike,
|
G_matrix: ArrayLike,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
tolerance: float = 1e-7,
|
tolerance: float = 1e-7,
|
||||||
max_iters: int = 10000,
|
max_iters: int = 10000,
|
||||||
reset_iters: int = 100,
|
reset_iters: int = 100,
|
||||||
@ -538,7 +539,7 @@ def eigsolve(
|
|||||||
`(eigenvalues, eigenvectors)` where `eigenvalues[i]` corresponds to the
|
`(eigenvalues, eigenvectors)` where `eigenvalues[i]` corresponds to the
|
||||||
vector `eigenvectors[i, :]`
|
vector `eigenvectors[i, :]`
|
||||||
"""
|
"""
|
||||||
k0 = numpy.array(k0, copy=False)
|
k0 = numpy.asarray(k0)
|
||||||
|
|
||||||
h_size = 2 * epsilon[0].size
|
h_size = 2 * epsilon[0].size
|
||||||
|
|
||||||
@ -561,11 +562,12 @@ def eigsolve(
|
|||||||
prev_theta = 0.5
|
prev_theta = 0.5
|
||||||
D = numpy.zeros(shape=y_shape, dtype=complex)
|
D = numpy.zeros(shape=y_shape, dtype=complex)
|
||||||
|
|
||||||
|
rng = numpy.random.default_rng()
|
||||||
Z: NDArray[numpy.complex128]
|
Z: NDArray[numpy.complex128]
|
||||||
if y0 is None:
|
if y0 is None:
|
||||||
Z = numpy.random.rand(*y_shape) + 1j * numpy.random.rand(*y_shape)
|
Z = rng.random(y_shape) + 1j * rng.random(y_shape)
|
||||||
else:
|
else:
|
||||||
Z = numpy.array(y0, copy=False).T
|
Z = numpy.asarray(y0).T
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
Z *= num_modes / norm(Z)
|
Z *= num_modes / norm(Z)
|
||||||
@ -573,7 +575,7 @@ def eigsolve(
|
|||||||
try:
|
try:
|
||||||
U = numpy.linalg.inv(ZtZ)
|
U = numpy.linalg.inv(ZtZ)
|
||||||
except numpy.linalg.LinAlgError:
|
except numpy.linalg.LinAlgError:
|
||||||
Z = numpy.random.rand(*y_shape) + 1j * numpy.random.rand(*y_shape)
|
Z = rng.random(y_shape) + 1j * rng.random(y_shape)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
trace_U = real(trace(U))
|
trace_U = real(trace(U))
|
||||||
@ -646,17 +648,16 @@ def eigsolve(
|
|||||||
|
|
||||||
Qi_memo: list[float | None] = [None, None]
|
Qi_memo: list[float | None] = [None, None]
|
||||||
|
|
||||||
def Qi_func(theta: float) -> float:
|
def Qi_func(theta: float, Qi_memo=Qi_memo, ZtZ=ZtZ, DtD=DtD, symZtD=symZtD) -> float: # noqa: ANN001
|
||||||
nonlocal Qi_memo
|
|
||||||
if Qi_memo[0] == theta:
|
if Qi_memo[0] == theta:
|
||||||
return cast(float, Qi_memo[1])
|
return cast('float', Qi_memo[1])
|
||||||
|
|
||||||
c = numpy.cos(theta)
|
c = numpy.cos(theta)
|
||||||
s = numpy.sin(theta)
|
s = numpy.sin(theta)
|
||||||
Q = c * c * ZtZ + s * s * DtD + 2 * s * c * symZtD
|
Q = c * c * ZtZ + s * s * DtD + 2 * s * c * symZtD
|
||||||
try:
|
try:
|
||||||
Qi = numpy.linalg.inv(Q)
|
Qi = numpy.linalg.inv(Q)
|
||||||
except numpy.linalg.LinAlgError:
|
except numpy.linalg.LinAlgError as err:
|
||||||
logger.info('taylor Qi')
|
logger.info('taylor Qi')
|
||||||
# if c or s small, taylor expand
|
# if c or s small, taylor expand
|
||||||
if c < 1e-4 * s and c != 0:
|
if c < 1e-4 * s and c != 0:
|
||||||
@ -666,12 +667,12 @@ def eigsolve(
|
|||||||
ZtZi = numpy.linalg.inv(ZtZ)
|
ZtZi = numpy.linalg.inv(ZtZ)
|
||||||
Qi = ZtZi / (c * c) - 2 * s / (c * c * c) * (ZtZi @ (ZtZi @ symZtD).conj().T)
|
Qi = ZtZi / (c * c) - 2 * s / (c * c * c) * (ZtZi @ (ZtZi @ symZtD).conj().T)
|
||||||
else:
|
else:
|
||||||
raise Exception('Inexplicable singularity in trace_func')
|
raise Exception('Inexplicable singularity in trace_func') from err
|
||||||
Qi_memo[0] = theta
|
Qi_memo[0] = theta
|
||||||
Qi_memo[1] = cast(float, Qi)
|
Qi_memo[1] = cast('float', Qi)
|
||||||
return cast(float, Qi)
|
return cast('float', Qi)
|
||||||
|
|
||||||
def trace_func(theta: float) -> float:
|
def trace_func(theta: float, ZtAZ=ZtAZ, DtAD=DtAD, symZtAD=symZtAD) -> float: # noqa: ANN001
|
||||||
c = numpy.cos(theta)
|
c = numpy.cos(theta)
|
||||||
s = numpy.sin(theta)
|
s = numpy.sin(theta)
|
||||||
Qi = Qi_func(theta)
|
Qi = Qi_func(theta)
|
||||||
@ -680,7 +681,7 @@ def eigsolve(
|
|||||||
return numpy.abs(trace)
|
return numpy.abs(trace)
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
def trace_deriv(theta):
|
def trace_deriv(theta, sgn: int = sgn, ZtAZ=ZtAZ, DtAD=DtAD, symZtD=symZtD, symZtAD=symZtAD, ZtZ=ZtZ, DtD=DtD): # noqa: ANN001
|
||||||
Qi = Qi_func(theta)
|
Qi = Qi_func(theta)
|
||||||
c2 = numpy.cos(2 * theta)
|
c2 = numpy.cos(2 * theta)
|
||||||
s2 = numpy.sin(2 * theta)
|
s2 = numpy.sin(2 * theta)
|
||||||
@ -799,3 +800,62 @@ def _rtrace_AtB(
|
|||||||
def _symmetrize(A: NDArray[numpy.complex128]) -> NDArray[numpy.complex128]:
|
def _symmetrize(A: NDArray[numpy.complex128]) -> NDArray[numpy.complex128]:
|
||||||
return (A + A.conj().T) * 0.5
|
return (A + A.conj().T) * 0.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def inner_product(
|
||||||
|
eL: cfdfield,
|
||||||
|
hL: cfdfield,
|
||||||
|
eR: cfdfield,
|
||||||
|
hR: cfdfield,
|
||||||
|
) -> complex:
|
||||||
|
# assumes x-axis propagation
|
||||||
|
|
||||||
|
assert numpy.array_equal(eR.shape, hR.shape)
|
||||||
|
assert numpy.array_equal(eL.shape, hL.shape)
|
||||||
|
assert numpy.array_equal(eR.shape, eL.shape)
|
||||||
|
|
||||||
|
# Cross product, times 2 since it's <p | n>, then divide by 4. # TODO might want to abs() this?
|
||||||
|
norm2R = (eR[1] * hR[2] - eR[2] * hR[1]).sum() / 2
|
||||||
|
norm2L = (eL[1] * hL[2] - eL[2] * hL[1]).sum() / 2
|
||||||
|
|
||||||
|
# eRxhR_x = numpy.cross(eR.reshape(3, -1), hR.reshape(3, -1), axis=0).reshape(eR.shape)[0] / normR
|
||||||
|
# logger.info(f'power {eRxhR_x.sum() / 2})
|
||||||
|
|
||||||
|
eR /= numpy.sqrt(norm2R)
|
||||||
|
hR /= numpy.sqrt(norm2R)
|
||||||
|
eL /= numpy.sqrt(norm2L)
|
||||||
|
hL /= numpy.sqrt(norm2L)
|
||||||
|
|
||||||
|
# (eR x hL)[0] and (eL x hR)[0]
|
||||||
|
eRxhL_x = eR[1] * hL[2] - eR[2] - hL[1]
|
||||||
|
eLxhR_x = eL[1] * hR[2] - eL[2] - hR[1]
|
||||||
|
|
||||||
|
#return 1j * (eRxhL_x - eLxhR_x).sum() / numpy.sqrt(norm2R * norm2L)
|
||||||
|
#return (eRxhL_x.sum() - eLxhR_x.sum()) / numpy.sqrt(norm2R * norm2L)
|
||||||
|
return eRxhL_x.sum() - eLxhR_x.sum()
|
||||||
|
|
||||||
|
|
||||||
|
def trq(
|
||||||
|
eI: cfdfield,
|
||||||
|
hI: cfdfield,
|
||||||
|
eO: cfdfield,
|
||||||
|
hO: cfdfield,
|
||||||
|
) -> tuple[complex, complex]:
|
||||||
|
pp = inner_product(eO, hO, eI, hI)
|
||||||
|
pn = inner_product(eO, hO, eI, -hI)
|
||||||
|
np = inner_product(eO, -hO, eI, hI)
|
||||||
|
nn = inner_product(eO, -hO, eI, -hI)
|
||||||
|
|
||||||
|
assert pp == -nn
|
||||||
|
assert pn == -np
|
||||||
|
|
||||||
|
logger.info(f'''
|
||||||
|
{pp=:4g} {pn=:4g}
|
||||||
|
{nn=:4g} {np=:4g}
|
||||||
|
{nn * pp / pn=:4g} {-np=:4g}
|
||||||
|
''')
|
||||||
|
|
||||||
|
r = -pp / pn # -<Pp|Bp>/<Pn/Bp> = -(-pp) / (-pn)
|
||||||
|
t = (np - nn * pp / pn) / 4
|
||||||
|
|
||||||
|
return t, r
|
||||||
|
|||||||
84
meanas/fdfd/eme.py
Normal file
84
meanas/fdfd/eme.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from collections.abc import Sequence
|
||||||
|
import numpy
|
||||||
|
from numpy.typing import NDArray
|
||||||
|
from scipy import sparse
|
||||||
|
|
||||||
|
from ..fdmath import dx_lists2_t, vcfdfield2
|
||||||
|
from .waveguide_2d import inner_product
|
||||||
|
|
||||||
|
|
||||||
|
def get_tr(
|
||||||
|
ehLs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_L: Sequence[complex],
|
||||||
|
ehRs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_R: Sequence[complex],
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
) -> tuple[NDArray[numpy.complex128], NDArray[numpy.complex128]]:
|
||||||
|
nL = len(wavenumbers_L)
|
||||||
|
nR = len(wavenumbers_R)
|
||||||
|
A12 = numpy.zeros((nL, nR), dtype=complex)
|
||||||
|
A21 = numpy.zeros((nL, nR), dtype=complex)
|
||||||
|
B11 = numpy.zeros((nL,), dtype=complex)
|
||||||
|
for ll in range(nL):
|
||||||
|
eL, hL = ehLs[ll]
|
||||||
|
B11[ll] = inner_product(eL, hL, dxes=dxes, conj_h=False)
|
||||||
|
for rr in range(nR):
|
||||||
|
eR, hR = ehRs[rr]
|
||||||
|
A12[ll, rr] = inner_product(eL, hR, dxes=dxes, conj_h=False) # TODO optimize loop?
|
||||||
|
A21[ll, rr] = inner_product(eR, hL, dxes=dxes, conj_h=False)
|
||||||
|
|
||||||
|
# tt0 = 2 * numpy.linalg.pinv(A21 + numpy.conj(A12))
|
||||||
|
tt0, _resid, _rank, _sing = numpy.linalg.lstsq(A21 + A12, numpy.diag(2 * B11), rcond=None)
|
||||||
|
|
||||||
|
U, st, V = numpy.linalg.svd(tt0)
|
||||||
|
gain = st > 1
|
||||||
|
st[gain] = 1 / st[gain]
|
||||||
|
tt = U @ numpy.diag(st) @ V
|
||||||
|
|
||||||
|
# rr = 0.5 * (A21 - numpy.conj(A12)) @ tt
|
||||||
|
rr = numpy.diag(0.5 / B11) @ (A21 - A12) @ tt
|
||||||
|
|
||||||
|
return tt, rr
|
||||||
|
|
||||||
|
|
||||||
|
def get_abcd(
|
||||||
|
ehLs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_L: Sequence[complex],
|
||||||
|
ehRs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_R: Sequence[complex],
|
||||||
|
**kwargs,
|
||||||
|
) -> sparse.sparray:
|
||||||
|
t12, r12 = get_tr(ehLs, wavenumbers_L, ehRs, wavenumbers_R, **kwargs)
|
||||||
|
t21, r21 = get_tr(ehRs, wavenumbers_R, ehLs, wavenumbers_L, **kwargs)
|
||||||
|
t21i = numpy.linalg.pinv(t21)
|
||||||
|
A = t12 - r21 @ t21i @ r12
|
||||||
|
B = r21 @ t21i
|
||||||
|
C = -t21i @ r12
|
||||||
|
D = t21i
|
||||||
|
return sparse.block_array(((A, B), (C, D)))
|
||||||
|
|
||||||
|
|
||||||
|
def get_s(
|
||||||
|
ehLs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_L: Sequence[complex],
|
||||||
|
ehRs: Sequence[Sequence[vcfdfield2]],
|
||||||
|
wavenumbers_R: Sequence[complex],
|
||||||
|
force_nogain: bool = False,
|
||||||
|
force_reciprocal: bool = False,
|
||||||
|
**kwargs,
|
||||||
|
) -> NDArray[numpy.complex128]:
|
||||||
|
t12, r12 = get_tr(ehLs, wavenumbers_L, ehRs, wavenumbers_R, **kwargs)
|
||||||
|
t21, r21 = get_tr(ehRs, wavenumbers_R, ehLs, wavenumbers_L, **kwargs)
|
||||||
|
|
||||||
|
ss = numpy.block([[r12, t12],
|
||||||
|
[t21, r21]])
|
||||||
|
|
||||||
|
if force_nogain:
|
||||||
|
# force S @ S.H diagonal
|
||||||
|
U, sing, V = numpy.linalg.svd(ss)
|
||||||
|
ss = numpy.diag(sing) @ U @ V
|
||||||
|
|
||||||
|
if force_reciprocal:
|
||||||
|
ss = 0.5 * (ss + ss.T)
|
||||||
|
|
||||||
|
return ss
|
||||||
@ -1,13 +1,16 @@
|
|||||||
"""
|
"""
|
||||||
Functions for performing near-to-farfield transformation (and the reverse).
|
Functions for performing near-to-farfield transformation (and the reverse).
|
||||||
"""
|
"""
|
||||||
from typing import Any, Sequence, cast
|
from typing import Any, cast, TYPE_CHECKING
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.fft import fft2, fftshift, fftfreq, ifft2, ifftshift
|
from numpy.fft import fft2, fftshift, fftfreq, ifft2, ifftshift
|
||||||
from numpy import pi
|
from numpy import pi
|
||||||
|
|
||||||
from ..fdmath import cfdfield_t
|
from ..fdmath import cfdfield_t
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
|
||||||
def near_to_farfield(
|
def near_to_farfield(
|
||||||
E_near: cfdfield_t,
|
E_near: cfdfield_t,
|
||||||
@ -62,7 +65,7 @@ def near_to_farfield(
|
|||||||
padded_size = (2**numpy.ceil(numpy.log2(s))).astype(int)
|
padded_size = (2**numpy.ceil(numpy.log2(s))).astype(int)
|
||||||
if not hasattr(padded_size, '__len__'):
|
if not hasattr(padded_size, '__len__'):
|
||||||
padded_size = (padded_size, padded_size) # type: ignore # checked if sequence
|
padded_size = (padded_size, padded_size) # type: ignore # checked if sequence
|
||||||
padded_shape = cast(Sequence[int], padded_size)
|
padded_shape = cast('Sequence[int]', padded_size)
|
||||||
|
|
||||||
En_fft = [fftshift(fft2(fftshift(Eni), s=padded_shape)) for Eni in E_near]
|
En_fft = [fftshift(fft2(fftshift(Eni), s=padded_shape)) for Eni in E_near]
|
||||||
Hn_fft = [fftshift(fft2(fftshift(Hni), s=padded_shape)) for Hni in H_near]
|
Hn_fft = [fftshift(fft2(fftshift(Hni), s=padded_shape)) for Hni in H_near]
|
||||||
@ -171,7 +174,7 @@ def far_to_nearfield(
|
|||||||
padded_size = (2 ** numpy.ceil(numpy.log2(s))).astype(int)
|
padded_size = (2 ** numpy.ceil(numpy.log2(s))).astype(int)
|
||||||
if not hasattr(padded_size, '__len__'):
|
if not hasattr(padded_size, '__len__'):
|
||||||
padded_size = (padded_size, padded_size) # type: ignore # checked if sequence
|
padded_size = (padded_size, padded_size) # type: ignore # checked if sequence
|
||||||
padded_shape = cast(Sequence[int], padded_size)
|
padded_shape = cast('Sequence[int]', padded_size)
|
||||||
|
|
||||||
k = 2 * pi
|
k = 2 * pi
|
||||||
kxs = fftshift(fftfreq(s[0], 1 / (s[0] * dkx)))
|
kxs = fftshift(fftfreq(s[0], 1 / (s[0] * dkx)))
|
||||||
|
|||||||
@ -5,10 +5,10 @@ Functional versions of many FDFD operators. These can be useful for performing
|
|||||||
The functions generated here expect `cfdfield_t` inputs with shape (3, X, Y, Z),
|
The functions generated here expect `cfdfield_t` inputs with shape (3, X, Y, Z),
|
||||||
e.g. E = [E_x, E_y, E_z] where each (complex) component has shape (X, Y, Z)
|
e.g. E = [E_x, E_y, E_z] where each (complex) component has shape (X, Y, Z)
|
||||||
"""
|
"""
|
||||||
from typing import Callable
|
from collections.abc import Callable
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from ..fdmath import dx_lists_t, fdfield_t, cfdfield_t, cfdfield_updater_t
|
from ..fdmath import dx_lists_t, cfdfield_t, fdfield, cfdfield, cfdfield_updater_t
|
||||||
from ..fdmath.functional import curl_forward, curl_back
|
from ..fdmath.functional import curl_forward, curl_back
|
||||||
|
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
def e_full(
|
def e_full(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> cfdfield_updater_t:
|
) -> cfdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Wave operator for use with E-field. See `operators.e_full` for details.
|
Wave operator for use with E-field. See `operators.e_full` for details.
|
||||||
@ -37,26 +37,25 @@ def e_full(
|
|||||||
ch = curl_back(dxes[1])
|
ch = curl_back(dxes[1])
|
||||||
ce = curl_forward(dxes[0])
|
ce = curl_forward(dxes[0])
|
||||||
|
|
||||||
def op_1(e: cfdfield_t) -> cfdfield_t:
|
def op_1(e: cfdfield) -> cfdfield_t:
|
||||||
curls = ch(ce(e))
|
curls = ch(ce(e))
|
||||||
return curls - omega ** 2 * epsilon * e
|
return cfdfield_t(curls - omega ** 2 * epsilon * e)
|
||||||
|
|
||||||
def op_mu(e: cfdfield_t) -> cfdfield_t:
|
def op_mu(e: cfdfield) -> cfdfield_t:
|
||||||
curls = ch(mu * ce(e)) # type: ignore # mu = None ok because we don't return the function
|
curls = ch(mu * ce(e)) # type: ignore # mu = None ok because we don't return the function
|
||||||
return curls - omega ** 2 * epsilon * e
|
return cfdfield_t(curls - omega ** 2 * epsilon * e)
|
||||||
|
|
||||||
if mu is None:
|
if mu is None:
|
||||||
return op_1
|
return op_1
|
||||||
else:
|
|
||||||
return op_mu
|
return op_mu
|
||||||
|
|
||||||
|
|
||||||
def eh_full(
|
def eh_full(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> Callable[[cfdfield_t, cfdfield_t], tuple[cfdfield_t, cfdfield_t]]:
|
) -> Callable[[cfdfield, cfdfield], tuple[cfdfield_t, cfdfield_t]]:
|
||||||
"""
|
"""
|
||||||
Wave operator for full (both E and H) field representation.
|
Wave operator for full (both E and H) field representation.
|
||||||
See `operators.eh_full`.
|
See `operators.eh_full`.
|
||||||
@ -74,24 +73,23 @@ def eh_full(
|
|||||||
ch = curl_back(dxes[1])
|
ch = curl_back(dxes[1])
|
||||||
ce = curl_forward(dxes[0])
|
ce = curl_forward(dxes[0])
|
||||||
|
|
||||||
def op_1(e: cfdfield_t, h: cfdfield_t) -> tuple[cfdfield_t, cfdfield_t]:
|
def op_1(e: cfdfield, h: cfdfield) -> tuple[cfdfield_t, cfdfield_t]:
|
||||||
return (ch(h) - 1j * omega * epsilon * e,
|
return (cfdfield_t(ch(h) - 1j * omega * epsilon * e),
|
||||||
ce(e) + 1j * omega * h)
|
cfdfield_t(ce(e) + 1j * omega * h))
|
||||||
|
|
||||||
def op_mu(e: cfdfield_t, h: cfdfield_t) -> tuple[cfdfield_t, cfdfield_t]:
|
def op_mu(e: cfdfield, h: cfdfield) -> tuple[cfdfield_t, cfdfield_t]:
|
||||||
return (ch(h) - 1j * omega * epsilon * e,
|
return (cfdfield_t(ch(h) - 1j * omega * epsilon * e),
|
||||||
ce(e) + 1j * omega * mu * h) # type: ignore # mu=None ok
|
cfdfield_t(ce(e) + 1j * omega * mu * h)) # type: ignore # mu=None ok
|
||||||
|
|
||||||
if mu is None:
|
if mu is None:
|
||||||
return op_1
|
return op_1
|
||||||
else:
|
|
||||||
return op_mu
|
return op_mu
|
||||||
|
|
||||||
|
|
||||||
def e2h(
|
def e2h(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> cfdfield_updater_t:
|
) -> cfdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting the `E` field into the `H` field.
|
Utility operator for converting the `E` field into the `H` field.
|
||||||
@ -108,22 +106,21 @@ def e2h(
|
|||||||
"""
|
"""
|
||||||
ce = curl_forward(dxes[0])
|
ce = curl_forward(dxes[0])
|
||||||
|
|
||||||
def e2h_1_1(e: cfdfield_t) -> cfdfield_t:
|
def e2h_1_1(e: cfdfield) -> cfdfield_t:
|
||||||
return ce(e) / (-1j * omega)
|
return cfdfield_t(ce(e) / (-1j * omega))
|
||||||
|
|
||||||
def e2h_mu(e: cfdfield_t) -> cfdfield_t:
|
def e2h_mu(e: cfdfield) -> cfdfield_t:
|
||||||
return ce(e) / (-1j * omega * mu) # type: ignore # mu=None ok
|
return cfdfield_t(ce(e) / (-1j * omega * mu)) # type: ignore # mu=None ok
|
||||||
|
|
||||||
if mu is None:
|
if mu is None:
|
||||||
return e2h_1_1
|
return e2h_1_1
|
||||||
else:
|
|
||||||
return e2h_mu
|
return e2h_mu
|
||||||
|
|
||||||
|
|
||||||
def m2j(
|
def m2j(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> cfdfield_updater_t:
|
) -> cfdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting magnetic current `M` distribution
|
Utility operator for converting magnetic current `M` distribution
|
||||||
@ -141,26 +138,25 @@ def m2j(
|
|||||||
"""
|
"""
|
||||||
ch = curl_back(dxes[1])
|
ch = curl_back(dxes[1])
|
||||||
|
|
||||||
def m2j_mu(m: cfdfield_t) -> cfdfield_t:
|
def m2j_mu(m: cfdfield) -> cfdfield_t:
|
||||||
J = ch(m / mu) / (-1j * omega) # type: ignore # mu=None ok
|
J = ch(m / mu) / (-1j * omega) # type: ignore # mu=None ok
|
||||||
return J
|
return cfdfield_t(J)
|
||||||
|
|
||||||
def m2j_1(m: cfdfield_t) -> cfdfield_t:
|
def m2j_1(m: cfdfield) -> cfdfield_t:
|
||||||
J = ch(m) / (-1j * omega)
|
J = ch(m) / (-1j * omega)
|
||||||
return J
|
return cfdfield_t(J)
|
||||||
|
|
||||||
if mu is None:
|
if mu is None:
|
||||||
return m2j_1
|
return m2j_1
|
||||||
else:
|
|
||||||
return m2j_mu
|
return m2j_mu
|
||||||
|
|
||||||
|
|
||||||
def e_tfsf_source(
|
def e_tfsf_source(
|
||||||
TF_region: fdfield_t,
|
TF_region: fdfield,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> cfdfield_updater_t:
|
) -> cfdfield_updater_t:
|
||||||
"""
|
"""
|
||||||
Operator that turns an E-field distribution into a total-field/scattered-field
|
Operator that turns an E-field distribution into a total-field/scattered-field
|
||||||
@ -182,13 +178,13 @@ def e_tfsf_source(
|
|||||||
# TODO documentation
|
# TODO documentation
|
||||||
A = e_full(omega, dxes, epsilon, mu)
|
A = e_full(omega, dxes, epsilon, mu)
|
||||||
|
|
||||||
def op(e: cfdfield_t) -> cfdfield_t:
|
def op(e: cfdfield) -> cfdfield_t:
|
||||||
neg_iwj = A(TF_region * e) - TF_region * A(e)
|
neg_iwj = A(TF_region * e) - TF_region * A(e)
|
||||||
return neg_iwj / (-1j * omega)
|
return cfdfield_t(neg_iwj / (-1j * omega))
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[cfdfield_t, cfdfield_t], cfdfield_t]:
|
def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[cfdfield, cfdfield], cfdfield_t]:
|
||||||
r"""
|
r"""
|
||||||
Generates a function that takes the single-frequency `E` and `H` fields
|
Generates a function that takes the single-frequency `E` and `H` fields
|
||||||
and calculates the cross product `E` x `H` = $E \times H$ as required
|
and calculates the cross product `E` x `H` = $E \times H$ as required
|
||||||
@ -210,7 +206,7 @@ def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[cfdfield_t, cfdfield_t], c
|
|||||||
Returns:
|
Returns:
|
||||||
Function `f` that returns E x H as required for the poynting vector.
|
Function `f` that returns E x H as required for the poynting vector.
|
||||||
"""
|
"""
|
||||||
def exh(e: cfdfield_t, h: cfdfield_t) -> cfdfield_t:
|
def exh(e: cfdfield, h: cfdfield) -> cfdfield_t:
|
||||||
s = numpy.empty_like(e)
|
s = numpy.empty_like(e)
|
||||||
ex = e[0] * dxes[0][0][:, None, None]
|
ex = e[0] * dxes[0][0][:, None, None]
|
||||||
ey = e[1] * dxes[0][1][None, :, None]
|
ey = e[1] * dxes[0][1][None, :, None]
|
||||||
@ -221,5 +217,5 @@ def poynting_e_cross_h(dxes: dx_lists_t) -> Callable[[cfdfield_t, cfdfield_t], c
|
|||||||
s[0] = numpy.roll(ey, -1, axis=0) * hz - numpy.roll(ez, -1, axis=0) * hy
|
s[0] = numpy.roll(ey, -1, axis=0) * hz - numpy.roll(ez, -1, axis=0) * hy
|
||||||
s[1] = numpy.roll(ez, -1, axis=1) * hx - numpy.roll(ex, -1, axis=1) * hz
|
s[1] = numpy.roll(ez, -1, axis=1) * hx - numpy.roll(ex, -1, axis=1) * hz
|
||||||
s[2] = numpy.roll(ex, -1, axis=2) * hy - numpy.roll(ey, -1, axis=2) * hx
|
s[2] = numpy.roll(ex, -1, axis=2) * hy - numpy.roll(ey, -1, axis=2) * hx
|
||||||
return s
|
return cfdfield_t(s)
|
||||||
return exh
|
return exh
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Sparse matrix operators for use with electromagnetic wave equations.
|
Sparse matrix operators for use with electromagnetic wave equations.
|
||||||
|
|
||||||
These functions return sparse-matrix (`scipy.sparse.spmatrix`) representations of
|
These functions return sparse-matrix (`scipy.sparse.sparray`) representations of
|
||||||
a variety of operators, intended for use with E and H fields vectorized using the
|
a variety of operators, intended for use with E and H fields vectorized using the
|
||||||
`meanas.fdmath.vectorization.vec()` and `meanas.fdmath.vectorization.unvec()` functions.
|
`meanas.fdmath.vectorization.vec()` and `meanas.fdmath.vectorization.unvec()` functions.
|
||||||
|
|
||||||
@ -28,9 +28,9 @@ The following operators are included:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import scipy.sparse as sparse # type: ignore
|
from scipy import sparse
|
||||||
|
|
||||||
from ..fdmath import vec, dx_lists_t, vfdfield_t, vcfdfield_t
|
from ..fdmath import vec, dx_lists_t, vfdfield, vcfdfield
|
||||||
from ..fdmath.operators import shift_with_mirror, shift_circ, curl_forward, curl_back
|
from ..fdmath.operators import shift_with_mirror, shift_circ, curl_forward, curl_back
|
||||||
|
|
||||||
|
|
||||||
@ -40,11 +40,11 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
def e_full(
|
def e_full(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield | vcfdfield,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
pec: vfdfield_t | None = None,
|
pec: vfdfield | None = None,
|
||||||
pmc: vfdfield_t | None = None,
|
pmc: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
r"""
|
r"""
|
||||||
Wave operator
|
Wave operator
|
||||||
$$ \nabla \times (\frac{1}{\mu} \nabla \times) - \Omega^2 \epsilon $$
|
$$ \nabla \times (\frac{1}{\mu} \nabla \times) - \Omega^2 \epsilon $$
|
||||||
@ -77,20 +77,20 @@ def e_full(
|
|||||||
ce = curl_forward(dxes[0])
|
ce = curl_forward(dxes[0])
|
||||||
|
|
||||||
if pec is None:
|
if pec is None:
|
||||||
pe = sparse.eye(epsilon.size)
|
pe = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pe = sparse.diags(numpy.where(pec, 0, 1)) # Set pe to (not PEC)
|
pe = sparse.diags_array(numpy.where(pec, 0, 1)) # Set pe to (not PEC)
|
||||||
|
|
||||||
if pmc is None:
|
if pmc is None:
|
||||||
pm = sparse.eye(epsilon.size)
|
pm = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pm = sparse.diags(numpy.where(pmc, 0, 1)) # set pm to (not PMC)
|
pm = sparse.diags_array(numpy.where(pmc, 0, 1)) # set pm to (not PMC)
|
||||||
|
|
||||||
e = sparse.diags(epsilon)
|
e = sparse.diags_array(epsilon)
|
||||||
if mu is None:
|
if mu is None:
|
||||||
m_div = sparse.eye(epsilon.size)
|
m_div = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
m_div = sparse.diags(1 / mu)
|
m_div = sparse.diags_array(1 / mu)
|
||||||
|
|
||||||
op = pe @ (ch @ pm @ m_div @ ce - omega**2 * e) @ pe
|
op = pe @ (ch @ pm @ m_div @ ce - omega**2 * e) @ pe
|
||||||
return op
|
return op
|
||||||
@ -98,7 +98,7 @@ def e_full(
|
|||||||
|
|
||||||
def e_full_preconditioners(
|
def e_full_preconditioners(
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
) -> tuple[sparse.spmatrix, sparse.spmatrix]:
|
) -> tuple[sparse.sparray, sparse.sparray]:
|
||||||
"""
|
"""
|
||||||
Left and right preconditioners `(Pl, Pr)` for symmetrizing the `e_full` wave operator.
|
Left and right preconditioners `(Pl, Pr)` for symmetrizing the `e_full` wave operator.
|
||||||
|
|
||||||
@ -118,19 +118,19 @@ def e_full_preconditioners(
|
|||||||
dxes[1][0][:, None, None] * dxes[1][1][None, :, None] * dxes[0][2][None, None, :]]
|
dxes[1][0][:, None, None] * dxes[1][1][None, :, None] * dxes[0][2][None, None, :]]
|
||||||
|
|
||||||
p_vector = numpy.sqrt(vec(p_squared))
|
p_vector = numpy.sqrt(vec(p_squared))
|
||||||
P_left = sparse.diags(p_vector)
|
P_left = sparse.diags_array(p_vector)
|
||||||
P_right = sparse.diags(1 / p_vector)
|
P_right = sparse.diags_array(1 / p_vector)
|
||||||
return P_left, P_right
|
return P_left, P_right
|
||||||
|
|
||||||
|
|
||||||
def h_full(
|
def h_full(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
pec: vfdfield_t | None = None,
|
pec: vfdfield | None = None,
|
||||||
pmc: vfdfield_t | None = None,
|
pmc: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
r"""
|
r"""
|
||||||
Wave operator
|
Wave operator
|
||||||
$$ \nabla \times (\frac{1}{\epsilon} \nabla \times) - \omega^2 \mu $$
|
$$ \nabla \times (\frac{1}{\epsilon} \nabla \times) - \omega^2 \mu $$
|
||||||
@ -161,20 +161,20 @@ def h_full(
|
|||||||
ce = curl_forward(dxes[0])
|
ce = curl_forward(dxes[0])
|
||||||
|
|
||||||
if pec is None:
|
if pec is None:
|
||||||
pe = sparse.eye(epsilon.size)
|
pe = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pe = sparse.diags(numpy.where(pec, 0, 1)) # set pe to (not PEC)
|
pe = sparse.diags_array(numpy.where(pec, 0, 1)) # set pe to (not PEC)
|
||||||
|
|
||||||
if pmc is None:
|
if pmc is None:
|
||||||
pm = sparse.eye(epsilon.size)
|
pm = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pm = sparse.diags(numpy.where(pmc, 0, 1)) # Set pe to (not PMC)
|
pm = sparse.diags_array(numpy.where(pmc, 0, 1)) # Set pe to (not PMC)
|
||||||
|
|
||||||
e_div = sparse.diags(1 / epsilon)
|
e_div = sparse.diags_array(1 / epsilon)
|
||||||
if mu is None:
|
if mu is None:
|
||||||
m = sparse.eye(epsilon.size)
|
m = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
m = sparse.diags(mu)
|
m = sparse.diags_array(mu)
|
||||||
|
|
||||||
A = pm @ (ce @ pe @ e_div @ ch - omega**2 * m) @ pm
|
A = pm @ (ce @ pe @ e_div @ ch - omega**2 * m) @ pm
|
||||||
return A
|
return A
|
||||||
@ -183,11 +183,11 @@ def h_full(
|
|||||||
def eh_full(
|
def eh_full(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
pec: vfdfield_t | None = None,
|
pec: vfdfield | None = None,
|
||||||
pmc: vfdfield_t | None = None,
|
pmc: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
r"""
|
r"""
|
||||||
Wave operator for `[E, H]` field representation. This operator implements Maxwell's
|
Wave operator for `[E, H]` field representation. This operator implements Maxwell's
|
||||||
equations without cancelling out either E or H. The operator is
|
equations without cancelling out either E or H. The operator is
|
||||||
@ -227,25 +227,25 @@ def eh_full(
|
|||||||
Sparse matrix containing the wave operator.
|
Sparse matrix containing the wave operator.
|
||||||
"""
|
"""
|
||||||
if pec is None:
|
if pec is None:
|
||||||
pe = sparse.eye(epsilon.size)
|
pe = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pe = sparse.diags(numpy.where(pec, 0, 1)) # set pe to (not PEC)
|
pe = sparse.diags_array(numpy.where(pec, 0, 1)) # set pe to (not PEC)
|
||||||
|
|
||||||
if pmc is None:
|
if pmc is None:
|
||||||
pm = sparse.eye(epsilon.size)
|
pm = sparse.eye_array(epsilon.size)
|
||||||
else:
|
else:
|
||||||
pm = sparse.diags(numpy.where(pmc, 0, 1)) # set pm to (not PMC)
|
pm = sparse.diags_array(numpy.where(pmc, 0, 1)) # set pm to (not PMC)
|
||||||
|
|
||||||
iwe = pe @ (1j * omega * sparse.diags(epsilon)) @ pe
|
iwe = pe @ (1j * omega * sparse.diags_array(epsilon)) @ pe
|
||||||
iwm = 1j * omega
|
iwm = 1j * omega
|
||||||
if mu is not None:
|
if mu is not None:
|
||||||
iwm *= sparse.diags(mu)
|
iwm *= sparse.diags_array(mu)
|
||||||
iwm = pm @ iwm @ pm
|
iwm = pm @ iwm @ pm
|
||||||
|
|
||||||
A1 = pe @ curl_back(dxes[1]) @ pm
|
A1 = pe @ curl_back(dxes[1]) @ pm
|
||||||
A2 = pm @ curl_forward(dxes[0]) @ pe
|
A2 = pm @ curl_forward(dxes[0]) @ pe
|
||||||
|
|
||||||
A = sparse.bmat([[-iwe, A1],
|
A = sparse.block_array([[-iwe, A1],
|
||||||
[A2, iwm]])
|
[A2, iwm]])
|
||||||
return A
|
return A
|
||||||
|
|
||||||
@ -253,9 +253,9 @@ def eh_full(
|
|||||||
def e2h(
|
def e2h(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
pmc: vfdfield_t | None = None,
|
pmc: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Utility operator for converting the E field into the H field.
|
Utility operator for converting the E field into the H field.
|
||||||
For use with `e_full()` -- assumes that there is no magnetic current M.
|
For use with `e_full()` -- assumes that there is no magnetic current M.
|
||||||
@ -274,10 +274,10 @@ def e2h(
|
|||||||
op = curl_forward(dxes[0]) / (-1j * omega)
|
op = curl_forward(dxes[0]) / (-1j * omega)
|
||||||
|
|
||||||
if mu is not None:
|
if mu is not None:
|
||||||
op = sparse.diags(1 / mu) @ op
|
op = sparse.diags_array(1 / mu) @ op
|
||||||
|
|
||||||
if pmc is not None:
|
if pmc is not None:
|
||||||
op = sparse.diags(numpy.where(pmc, 0, 1)) @ op
|
op = sparse.diags_array(numpy.where(pmc, 0, 1)) @ op
|
||||||
|
|
||||||
return op
|
return op
|
||||||
|
|
||||||
@ -285,8 +285,8 @@ def e2h(
|
|||||||
def m2j(
|
def m2j(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator for converting a magnetic current M into an electric current J.
|
Operator for converting a magnetic current M into an electric current J.
|
||||||
For use with eg. `e_full()`.
|
For use with eg. `e_full()`.
|
||||||
@ -302,12 +302,12 @@ def m2j(
|
|||||||
op = curl_back(dxes[1]) / (1j * omega)
|
op = curl_back(dxes[1]) / (1j * omega)
|
||||||
|
|
||||||
if mu is not None:
|
if mu is not None:
|
||||||
op = op @ sparse.diags(1 / mu)
|
op = op @ sparse.diags_array(1 / mu)
|
||||||
|
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def poynting_e_cross(e: vcfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
def poynting_e_cross(e: vcfdfield, dxes: dx_lists_t) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator for computing the Poynting vector, containing the
|
Operator for computing the Poynting vector, containing the
|
||||||
(E x) portion of the Poynting vector.
|
(E x) portion of the Poynting vector.
|
||||||
@ -321,22 +321,22 @@ def poynting_e_cross(e: vcfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
"""
|
"""
|
||||||
shape = [len(dx) for dx in dxes[0]]
|
shape = [len(dx) for dx in dxes[0]]
|
||||||
|
|
||||||
fx, fy, fz = [shift_circ(i, shape, 1) for i in range(3)]
|
fx, fy, fz = (shift_circ(i, shape, 1) for i in range(3))
|
||||||
|
|
||||||
dxag = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[0], indexing='ij')]
|
dxag = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[0], indexing='ij')]
|
||||||
dxbg = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[1], indexing='ij')]
|
dxbg = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[1], indexing='ij')]
|
||||||
Ex, Ey, Ez = [ei * da for ei, da in zip(numpy.split(e, 3), dxag)]
|
Ex, Ey, Ez = (ei * da for ei, da in zip(numpy.split(e, 3), dxag, strict=True))
|
||||||
|
|
||||||
block_diags = [[ None, fx @ -Ez, fx @ Ey],
|
block_diags = [[ None, fx @ -Ez, fx @ Ey],
|
||||||
[ fy @ Ez, None, fy @ -Ex],
|
[ fy @ Ez, None, fy @ -Ex],
|
||||||
[ fz @ -Ey, fz @ Ex, None]]
|
[ fz @ -Ey, fz @ Ex, None]]
|
||||||
block_matrix = sparse.bmat([[sparse.diags(x) if x is not None else None for x in row]
|
block_matrix = sparse.block_array([[sparse.diags_array(x) if x is not None else None for x in row]
|
||||||
for row in block_diags])
|
for row in block_diags])
|
||||||
P = block_matrix @ sparse.diags(numpy.concatenate(dxbg))
|
P = block_matrix @ sparse.diags_array(numpy.concatenate(dxbg))
|
||||||
return P
|
return P
|
||||||
|
|
||||||
|
|
||||||
def poynting_h_cross(h: vcfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
def poynting_h_cross(h: vcfdfield, dxes: dx_lists_t) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator for computing the Poynting vector, containing the (H x) portion of the Poynting vector.
|
Operator for computing the Poynting vector, containing the (H x) portion of the Poynting vector.
|
||||||
|
|
||||||
@ -349,27 +349,27 @@ def poynting_h_cross(h: vcfdfield_t, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
"""
|
"""
|
||||||
shape = [len(dx) for dx in dxes[0]]
|
shape = [len(dx) for dx in dxes[0]]
|
||||||
|
|
||||||
fx, fy, fz = [shift_circ(i, shape, 1) for i in range(3)]
|
fx, fy, fz = (shift_circ(i, shape, 1) for i in range(3))
|
||||||
|
|
||||||
dxag = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[0], indexing='ij')]
|
dxag = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[0], indexing='ij')]
|
||||||
dxbg = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[1], indexing='ij')]
|
dxbg = [dx.ravel(order='C') for dx in numpy.meshgrid(*dxes[1], indexing='ij')]
|
||||||
Hx, Hy, Hz = [sparse.diags(hi * db) for hi, db in zip(numpy.split(h, 3), dxbg)]
|
Hx, Hy, Hz = (sparse.diags_array(hi * db) for hi, db in zip(numpy.split(h, 3), dxbg, strict=True))
|
||||||
|
|
||||||
P = (sparse.bmat(
|
P = (sparse.block_array(
|
||||||
[[ None, -Hz @ fx, Hy @ fx],
|
[[ None, -Hz @ fx, Hy @ fx],
|
||||||
[ Hz @ fy, None, -Hx @ fy],
|
[ Hz @ fy, None, -Hx @ fy],
|
||||||
[-Hy @ fz, Hx @ fz, None]])
|
[-Hy @ fz, Hx @ fz, None]])
|
||||||
@ sparse.diags(numpy.concatenate(dxag)))
|
@ sparse.diags_array(numpy.concatenate(dxag)))
|
||||||
return P
|
return P
|
||||||
|
|
||||||
|
|
||||||
def e_tfsf_source(
|
def e_tfsf_source(
|
||||||
TF_region: vfdfield_t,
|
TF_region: vfdfield,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator that turns a desired E-field distribution into a
|
Operator that turns a desired E-field distribution into a
|
||||||
total-field/scattered-field (TFSF) source.
|
total-field/scattered-field (TFSF) source.
|
||||||
@ -390,18 +390,18 @@ def e_tfsf_source(
|
|||||||
"""
|
"""
|
||||||
# TODO documentation
|
# TODO documentation
|
||||||
A = e_full(omega, dxes, epsilon, mu)
|
A = e_full(omega, dxes, epsilon, mu)
|
||||||
Q = sparse.diags(TF_region)
|
Q = sparse.diags_array(TF_region)
|
||||||
return (A @ Q - Q @ A) / (-1j * omega)
|
return (A @ Q - Q @ A) / (-1j * omega)
|
||||||
|
|
||||||
|
|
||||||
def e_boundary_source(
|
def e_boundary_source(
|
||||||
mask: vfdfield_t,
|
mask: vfdfield,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdfield | None = None,
|
||||||
periodic_mask_edges: bool = False,
|
periodic_mask_edges: bool = False,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator that turns an E-field distrubtion into a current (J) distribution
|
Operator that turns an E-field distrubtion into a current (J) distribution
|
||||||
along the edges (external and internal) of the provided mask. This is just an
|
along the edges (external and internal) of the provided mask. This is just an
|
||||||
@ -424,10 +424,10 @@ def e_boundary_source(
|
|||||||
shape = [len(dxe) for dxe in dxes[0]]
|
shape = [len(dxe) for dxe in dxes[0]]
|
||||||
jmask = numpy.zeros_like(mask, dtype=bool)
|
jmask = numpy.zeros_like(mask, dtype=bool)
|
||||||
|
|
||||||
def shift_rot(axis: int, polarity: int) -> sparse.spmatrix:
|
def shift_rot(axis: int, polarity: int) -> sparse.sparray:
|
||||||
return shift_circ(axis=axis, shape=shape, shift_distance=polarity)
|
return shift_circ(axis=axis, shape=shape, shift_distance=polarity)
|
||||||
|
|
||||||
def shift_mir(axis: int, polarity: int) -> sparse.spmatrix:
|
def shift_mir(axis: int, polarity: int) -> sparse.sparray:
|
||||||
return shift_with_mirror(axis=axis, shape=shape, shift_distance=polarity)
|
return shift_with_mirror(axis=axis, shape=shape, shift_distance=polarity)
|
||||||
|
|
||||||
shift = shift_rot if periodic_mask_edges else shift_mir
|
shift = shift_rot if periodic_mask_edges else shift_mir
|
||||||
@ -436,7 +436,7 @@ def e_boundary_source(
|
|||||||
if shape[axis] == 1:
|
if shape[axis] == 1:
|
||||||
continue
|
continue
|
||||||
for polarity in (-1, +1):
|
for polarity in (-1, +1):
|
||||||
r = shift(axis, polarity) - sparse.eye(numpy.prod(shape)) # shifted minus original
|
r = shift(axis, polarity) - sparse.eye_array(numpy.prod(shape)) # shifted minus original
|
||||||
r3 = sparse.block_diag((r, r, r))
|
r3 = sparse.block_diag((r, r, r))
|
||||||
jmask = numpy.logical_or(jmask, numpy.abs(r3 @ mask))
|
jmask = numpy.logical_or(jmask, numpy.abs(r3 @ mask))
|
||||||
|
|
||||||
@ -447,5 +447,5 @@ def e_boundary_source(
|
|||||||
# (numpy.roll(mask, -1, axis=2) != mask) |
|
# (numpy.roll(mask, -1, axis=2) != mask) |
|
||||||
# (numpy.roll(mask, +1, axis=2) != mask))
|
# (numpy.roll(mask, +1, axis=2) != mask))
|
||||||
|
|
||||||
return sparse.diags(jmask.astype(int)) @ full
|
return sparse.diags_array(jmask.astype(int)) @ full
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
Functions for creating stretched coordinate perfectly matched layer (PML) absorbers.
|
Functions for creating stretched coordinate perfectly matched layer (PML) absorbers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Sequence, Callable
|
from collections.abc import Sequence, Callable
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|||||||
@ -2,15 +2,16 @@
|
|||||||
Solvers and solver interface for FDFD problems.
|
Solvers and solver interface for FDFD problems.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Callable, Dict, Any, Optional
|
from typing import Any
|
||||||
|
from collections.abc import Callable
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike, NDArray
|
||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.sparse.linalg # type: ignore
|
import scipy.sparse.linalg
|
||||||
|
|
||||||
from ..fdmath import dx_lists_t, vfdfield_t, vcfdfield_t
|
from ..fdmath import dx_lists_t, vfdfield, vcfdfield, vcfdfield_t
|
||||||
from . import operators
|
from . import operators
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def _scipy_qmr(
|
def _scipy_qmr(
|
||||||
A: scipy.sparse.csr_matrix,
|
A: scipy.sparse.csr_array,
|
||||||
b: ArrayLike,
|
b: ArrayLike,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> NDArray[numpy.float64]:
|
) -> NDArray[numpy.float64]:
|
||||||
@ -34,16 +35,16 @@ def _scipy_qmr(
|
|||||||
Guess for solution (returned even if didn't converge)
|
Guess for solution (returned even if didn't converge)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
'''
|
#
|
||||||
Report on our progress
|
#Report on our progress
|
||||||
'''
|
#
|
||||||
ii = 0
|
ii = 0
|
||||||
|
|
||||||
def log_residual(xk: ArrayLike) -> None:
|
def log_residual(xk: ArrayLike) -> None:
|
||||||
nonlocal ii
|
nonlocal ii
|
||||||
ii += 1
|
ii += 1
|
||||||
if ii % 100 == 0:
|
if ii % 100 == 0:
|
||||||
cur_norm = norm(A @ xk - b)
|
cur_norm = norm(A @ xk - b) / norm(b)
|
||||||
logger.info(f'Solver residual at iteration {ii} : {cur_norm}')
|
logger.info(f'Solver residual at iteration {ii} : {cur_norm}')
|
||||||
|
|
||||||
if 'callback' in kwargs:
|
if 'callback' in kwargs:
|
||||||
@ -55,10 +56,9 @@ def _scipy_qmr(
|
|||||||
else:
|
else:
|
||||||
kwargs['callback'] = log_residual
|
kwargs['callback'] = log_residual
|
||||||
|
|
||||||
'''
|
#
|
||||||
Run the actual solve
|
# Run the actual solve
|
||||||
'''
|
#
|
||||||
|
|
||||||
x, _ = scipy.sparse.linalg.qmr(A, b, **kwargs)
|
x, _ = scipy.sparse.linalg.qmr(A, b, **kwargs)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
@ -66,14 +66,16 @@ def _scipy_qmr(
|
|||||||
def generic(
|
def generic(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
J: vcfdfield_t,
|
J: vcfdfield,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdfield,
|
||||||
mu: Optional[vfdfield_t] = None,
|
mu: vfdfield | None = None,
|
||||||
pec: Optional[vfdfield_t] = None,
|
*,
|
||||||
pmc: Optional[vfdfield_t] = None,
|
pec: vfdfield | None = None,
|
||||||
|
pmc: vfdfield | None = None,
|
||||||
adjoint: bool = False,
|
adjoint: bool = False,
|
||||||
matrix_solver: Callable[..., ArrayLike] = _scipy_qmr,
|
matrix_solver: Callable[..., ArrayLike] = _scipy_qmr,
|
||||||
matrix_solver_opts: Optional[Dict[str, Any]] = None,
|
matrix_solver_opts: dict[str, Any] | None = None,
|
||||||
|
E_guess: vcfdfield | None = None,
|
||||||
) -> vcfdfield_t:
|
) -> vcfdfield_t:
|
||||||
"""
|
"""
|
||||||
Conjugate gradient FDFD solver using CSR sparse matrices.
|
Conjugate gradient FDFD solver using CSR sparse matrices.
|
||||||
@ -93,13 +95,15 @@ def generic(
|
|||||||
(at H-field locations; non-zero value indicates PMC is present)
|
(at H-field locations; non-zero value indicates PMC is present)
|
||||||
adjoint: If true, solves the adjoint problem.
|
adjoint: If true, solves the adjoint problem.
|
||||||
matrix_solver: Called as `matrix_solver(A, b, **matrix_solver_opts) -> x`,
|
matrix_solver: Called as `matrix_solver(A, b, **matrix_solver_opts) -> x`,
|
||||||
where `A`: `scipy.sparse.csr_matrix`;
|
where `A`: `scipy.sparse.csr_array`;
|
||||||
`b`: `ArrayLike`;
|
`b`: `ArrayLike`;
|
||||||
`x`: `ArrayLike`;
|
`x`: `ArrayLike`;
|
||||||
Default is a wrapped version of `scipy.sparse.linalg.qmr()`
|
Default is a wrapped version of `scipy.sparse.linalg.qmr()`
|
||||||
which doesn't return convergence info and logs the residual
|
which doesn't return convergence info and logs the residual
|
||||||
every 100 iterations.
|
every 100 iterations.
|
||||||
matrix_solver_opts: Passed as kwargs to `matrix_solver(...)`
|
matrix_solver_opts: Passed as kwargs to `matrix_solver(...)`
|
||||||
|
E_guess: Guess at the solution E-field. `matrix_solver` must accept an
|
||||||
|
`x0` argument with the same purpose.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
E-field which solves the system.
|
E-field which solves the system.
|
||||||
@ -120,6 +124,13 @@ def generic(
|
|||||||
A = Pl @ A0 @ Pr
|
A = Pl @ A0 @ Pr
|
||||||
b = Pl @ b0
|
b = Pl @ b0
|
||||||
|
|
||||||
|
if E_guess is not None:
|
||||||
|
if adjoint:
|
||||||
|
x0 = Pr.H @ E_guess
|
||||||
|
else:
|
||||||
|
x0 = Pl @ E_guess
|
||||||
|
matrix_solver_opts['x0'] = x0
|
||||||
|
|
||||||
x = matrix_solver(A.tocsr(), b, **matrix_solver_opts)
|
x = matrix_solver(A.tocsr(), b, **matrix_solver_opts)
|
||||||
|
|
||||||
if adjoint:
|
if adjoint:
|
||||||
@ -127,4 +138,4 @@ def generic(
|
|||||||
else:
|
else:
|
||||||
x0 = Pr @ x
|
x0 = Pr @ x
|
||||||
|
|
||||||
return x0
|
return vcfdfield_t(x0)
|
||||||
|
|||||||
@ -18,8 +18,8 @@ $$
|
|||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
\nabla \times \vec{E}(x, y, z) &= -\imath \omega \mu \vec{H} \\
|
\nabla \times \vec{E}(x, y, z) &= -\imath \omega \mu \vec{H} \\
|
||||||
\nabla \times \vec{H}(x, y, z) &= \imath \omega \epsilon \vec{E} \\
|
\nabla \times \vec{H}(x, y, z) &= \imath \omega \epsilon \vec{E} \\
|
||||||
\vec{E}(x,y,z) &= (\vec{E}_t(x, y) + E_z(x, y)\vec{z}) e^{-\gamma z} \\
|
\vec{E}(x,y,z) &= (\vec{E}_t(x, y) + E_z(x, y)\vec{z}) e^{-\imath \beta z} \\
|
||||||
\vec{H}(x,y,z) &= (\vec{H}_t(x, y) + H_z(x, y)\vec{z}) e^{-\gamma z} \\
|
\vec{H}(x,y,z) &= (\vec{H}_t(x, y) + H_z(x, y)\vec{z}) e^{-\imath \beta z} \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
@ -40,56 +40,57 @@ Substituting in our expressions for $\vec{E}$, $\vec{H}$ and discretizing:
|
|||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\imath \omega \mu_{xx} H_x &= \tilde{\partial}_y E_z + \gamma E_y \\
|
-\imath \omega \mu_{xx} H_x &= \tilde{\partial}_y E_z + \imath \beta E_y \\
|
||||||
-\imath \omega \mu_{yy} H_y &= -\gamma E_x - \tilde{\partial}_x E_z \\
|
-\imath \omega \mu_{yy} H_y &= -\imath \beta E_x - \tilde{\partial}_x E_z \\
|
||||||
-\imath \omega \mu_{zz} H_z &= \tilde{\partial}_x E_y - \tilde{\partial}_y E_x \\
|
-\imath \omega \mu_{zz} H_z &= \tilde{\partial}_x E_y - \tilde{\partial}_y E_x \\
|
||||||
\imath \omega \epsilon_{xx} E_x &= \hat{\partial}_y H_z + \gamma H_y \\
|
\imath \omega \epsilon_{xx} E_x &= \hat{\partial}_y H_z + \imath \beta H_y \\
|
||||||
\imath \omega \epsilon_{yy} E_y &= -\gamma H_x - \hat{\partial}_x H_z \\
|
\imath \omega \epsilon_{yy} E_y &= -\imath \beta H_x - \hat{\partial}_x H_z \\
|
||||||
\imath \omega \epsilon_{zz} E_z &= \hat{\partial}_x H_y - \hat{\partial}_y H_x \\
|
\imath \omega \epsilon_{zz} E_z &= \hat{\partial}_x H_y - \hat{\partial}_y H_x \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
Rewrite the last three equations as
|
Rewrite the last three equations as
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
\gamma H_y &= \imath \omega \epsilon_{xx} E_x - \hat{\partial}_y H_z \\
|
\imath \beta H_y &= \imath \omega \epsilon_{xx} E_x - \hat{\partial}_y H_z \\
|
||||||
\gamma H_x &= -\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z \\
|
\imath \beta H_x &= -\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z \\
|
||||||
\imath \omega E_z &= \frac{1}{\epsilon_{zz}} \hat{\partial}_x H_y - \frac{1}{\epsilon_{zz}} \hat{\partial}_y H_x \\
|
\imath \omega E_z &= \frac{1}{\epsilon_{zz}} \hat{\partial}_x H_y - \frac{1}{\epsilon_{zz}} \hat{\partial}_y H_x \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
Now apply $\gamma \tilde{\partial}_x$ to the last equation,
|
Now apply $\imath \beta \tilde{\partial}_x$ to the last equation,
|
||||||
then substitute in for $\gamma H_x$ and $\gamma H_y$:
|
then substitute in for $\imath \beta H_x$ and $\imath \beta H_y$:
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
\gamma \tilde{\partial}_x \imath \omega E_z &= \gamma \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x H_y
|
\imath \beta \tilde{\partial}_x \imath \omega E_z &= \imath \beta \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x H_y
|
||||||
- \gamma \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y H_x \\
|
- \imath \beta \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y H_x \\
|
||||||
&= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x ( \imath \omega \epsilon_{xx} E_x - \hat{\partial}_y H_z)
|
&= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x ( \imath \omega \epsilon_{xx} E_x - \hat{\partial}_y H_z)
|
||||||
- \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (-\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z) \\
|
- \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (-\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z) \\
|
||||||
&= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x ( \imath \omega \epsilon_{xx} E_x)
|
&= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x ( \imath \omega \epsilon_{xx} E_x)
|
||||||
- \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (-\imath \omega \epsilon_{yy} E_y) \\
|
- \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (-\imath \omega \epsilon_{yy} E_y) \\
|
||||||
\gamma \tilde{\partial}_x E_z &= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\imath \beta \tilde{\partial}_x E_z &= \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y) \\
|
+ \tilde{\partial}_x \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y) \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
With a similar approach (but using $\gamma \tilde{\partial}_y$ instead), we can get
|
With a similar approach (but using $\imath \beta \tilde{\partial}_y$ instead), we can get
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
\gamma \tilde{\partial}_y E_z &= \tilde{\partial}_y \frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\imath \beta \tilde{\partial}_y E_z &= \tilde{\partial}_y \frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \tilde{\partial}_y \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y) \\
|
+ \tilde{\partial}_y \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y) \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
We can combine this equation for $\gamma \tilde{\partial}_y E_z$ with
|
We can combine this equation for $\imath \beta \tilde{\partial}_y E_z$ with
|
||||||
the unused $\imath \omega \mu_{xx} H_x$ and $\imath \omega \mu_{yy} H_y$ equations to get
|
the unused $\imath \omega \mu_{xx} H_x$ and $\imath \omega \mu_{yy} H_y$ equations to get
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\imath \omega \mu_{xx} \gamma H_x &= \gamma^2 E_y + \gamma \tilde{\partial}_y E_z \\
|
-\imath \omega \mu_{xx} \imath \beta H_x &= -\beta^2 E_y + \imath \beta \tilde{\partial}_y E_z \\
|
||||||
-\imath \omega \mu_{xx} \gamma H_x &= \gamma^2 E_y + \tilde{\partial}_y (
|
-\imath \omega \mu_{xx} \imath \beta H_x &= -\beta^2 E_y + \tilde{\partial}_y (
|
||||||
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
||||||
)\\
|
)\\
|
||||||
@ -100,22 +101,21 @@ and
|
|||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\imath \omega \mu_{yy} \gamma H_y &= -\gamma^2 E_x - \gamma \tilde{\partial}_x E_z \\
|
-\imath \omega \mu_{yy} \imath \beta H_y &= \beta^2 E_x - \imath \beta \tilde{\partial}_x E_z \\
|
||||||
-\imath \omega \mu_{yy} \gamma H_y &= -\gamma^2 E_x - \tilde{\partial}_x (
|
-\imath \omega \mu_{yy} \imath \beta H_y &= \beta^2 E_x - \tilde{\partial}_x (
|
||||||
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
||||||
)\\
|
)\\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
However, based on our rewritten equation for $\gamma H_x$ and the so-far unused
|
However, based on our rewritten equation for $\imath \beta H_x$ and the so-far unused
|
||||||
equation for $\imath \omega \mu_{zz} H_z$ we can also write
|
equation for $\imath \omega \mu_{zz} H_z$ we can also write
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\imath \omega \mu_{xx} (\gamma H_x) &= -\imath \omega \mu_{xx} (-\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z) \\
|
-\imath \omega \mu_{xx} (\imath \beta H_x) &= -\imath \omega \mu_{xx} (-\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z) \\
|
||||||
&= -\omega^2 \mu_{xx} \epsilon_{yy} E_y
|
&= -\omega^2 \mu_{xx} \epsilon_{yy} E_y + \imath \omega \mu_{xx} \hat{\partial}_x (
|
||||||
+\imath \omega \mu_{xx} \hat{\partial}_x (
|
|
||||||
\frac{1}{-\imath \omega \mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x)) \\
|
\frac{1}{-\imath \omega \mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x)) \\
|
||||||
&= -\omega^2 \mu_{xx} \epsilon_{yy} E_y
|
&= -\omega^2 \mu_{xx} \epsilon_{yy} E_y
|
||||||
-\mu_{xx} \hat{\partial}_x \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
-\mu_{xx} \hat{\partial}_x \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
||||||
@ -126,7 +126,7 @@ and, similarly,
|
|||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\imath \omega \mu_{yy} (\gamma H_y) &= \omega^2 \mu_{yy} \epsilon_{xx} E_x
|
-\imath \omega \mu_{yy} (\imath \beta H_y) &= \omega^2 \mu_{yy} \epsilon_{xx} E_x
|
||||||
+\mu_{yy} \hat{\partial}_y \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
+\mu_{yy} \hat{\partial}_y \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
$$
|
$$
|
||||||
@ -135,12 +135,12 @@ By combining both pairs of expressions, we get
|
|||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
-\gamma^2 E_x - \tilde{\partial}_x (
|
\beta^2 E_x - \tilde{\partial}_x (
|
||||||
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
||||||
) &= \omega^2 \mu_{yy} \epsilon_{xx} E_x
|
) &= \omega^2 \mu_{yy} \epsilon_{xx} E_x
|
||||||
+\mu_{yy} \hat{\partial}_y \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
+\mu_{yy} \hat{\partial}_y \frac{1}{\mu_{zz}} (\tilde{\partial}_x E_y - \tilde{\partial}_y E_x) \\
|
||||||
\gamma^2 E_y + \tilde{\partial}_y (
|
-\beta^2 E_y + \tilde{\partial}_y (
|
||||||
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
\frac{1}{\epsilon_{zz}} \hat{\partial}_x (\epsilon_{xx} E_x)
|
||||||
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
+ \frac{1}{\epsilon_{zz}} \hat{\partial}_y (\epsilon_{yy} E_y)
|
||||||
) &= -\omega^2 \mu_{xx} \epsilon_{yy} E_y
|
) &= -\omega^2 \mu_{xx} \epsilon_{yy} E_y
|
||||||
@ -165,27 +165,27 @@ $$
|
|||||||
E_y \end{bmatrix}
|
E_y \end{bmatrix}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
where $\gamma = \imath\beta$. In the literature, $\beta$ is usually used to denote
|
In the literature, $\beta$ is usually used to denote the lossless/real part of the propagation constant,
|
||||||
the lossless/real part of the propagation constant, but in `meanas` it is allowed to
|
but in `meanas` it is allowed to be complex.
|
||||||
be complex.
|
|
||||||
|
|
||||||
An equivalent eigenvalue problem can be formed using the $H_x$ and $H_y$ fields, if those are more convenient.
|
An equivalent eigenvalue problem can be formed using the $H_x$ and $H_y$ fields, if those are more convenient.
|
||||||
|
|
||||||
Note that $E_z$ was never discretized, so $\gamma$ and $\beta$ will need adjustment
|
Note that $E_z$ was never discretized, so $\beta$ will need adjustment to account for numerical dispersion
|
||||||
to account for numerical dispersion if the result is introduced into a space with a discretized z-axis.
|
if the result is introduced into a space with a discretized z-axis.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# TODO update module docs
|
# TODO update module docs
|
||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from collections.abc import Sequence
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray, ArrayLike
|
from numpy.typing import NDArray
|
||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.sparse as sparse # type: ignore
|
from scipy import sparse
|
||||||
|
|
||||||
from ..fdmath.operators import deriv_forward, deriv_back, cross
|
from ..fdmath.operators import deriv_forward, deriv_back, cross
|
||||||
from ..fdmath import vec, unvec, dx_lists_t, vfdfield_t, vcfdfield_t
|
from ..fdmath import vec, unvec, dx_lists2_t, vcfdfield2_t, vcfdslice_t, vcfdfield2, vfdslice, vcfdslice
|
||||||
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||||
|
|
||||||
|
|
||||||
@ -194,10 +194,10 @@ __author__ = 'Jan Petykiewicz'
|
|||||||
|
|
||||||
def operator_e(
|
def operator_e(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
r"""
|
r"""
|
||||||
Waveguide operator of the form
|
Waveguide operator of the form
|
||||||
|
|
||||||
@ -246,12 +246,12 @@ def operator_e(
|
|||||||
Dbx, Dby = deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
|
||||||
eps_parts = numpy.split(epsilon, 3)
|
eps_parts = numpy.split(epsilon, 3)
|
||||||
eps_xy = sparse.diags(numpy.hstack((eps_parts[0], eps_parts[1])))
|
eps_xy = sparse.diags_array(numpy.hstack((eps_parts[0], eps_parts[1])))
|
||||||
eps_z_inv = sparse.diags(1 / eps_parts[2])
|
eps_z_inv = sparse.diags_array(1 / eps_parts[2])
|
||||||
|
|
||||||
mu_parts = numpy.split(mu, 3)
|
mu_parts = numpy.split(mu, 3)
|
||||||
mu_yx = sparse.diags(numpy.hstack((mu_parts[1], mu_parts[0])))
|
mu_yx = sparse.diags_array(numpy.hstack((mu_parts[1], mu_parts[0])))
|
||||||
mu_z_inv = sparse.diags(1 / mu_parts[2])
|
mu_z_inv = sparse.diags_array(1 / mu_parts[2])
|
||||||
|
|
||||||
op = (
|
op = (
|
||||||
omega * omega * mu_yx @ eps_xy
|
omega * omega * mu_yx @ eps_xy
|
||||||
@ -263,10 +263,10 @@ def operator_e(
|
|||||||
|
|
||||||
def operator_h(
|
def operator_h(
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
r"""
|
r"""
|
||||||
Waveguide operator of the form
|
Waveguide operator of the form
|
||||||
|
|
||||||
@ -315,12 +315,12 @@ def operator_h(
|
|||||||
Dbx, Dby = deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
|
||||||
eps_parts = numpy.split(epsilon, 3)
|
eps_parts = numpy.split(epsilon, 3)
|
||||||
eps_yx = sparse.diags(numpy.hstack((eps_parts[1], eps_parts[0])))
|
eps_yx = sparse.diags_array(numpy.hstack((eps_parts[1], eps_parts[0])))
|
||||||
eps_z_inv = sparse.diags(1 / eps_parts[2])
|
eps_z_inv = sparse.diags_array(1 / eps_parts[2])
|
||||||
|
|
||||||
mu_parts = numpy.split(mu, 3)
|
mu_parts = numpy.split(mu, 3)
|
||||||
mu_xy = sparse.diags(numpy.hstack((mu_parts[0], mu_parts[1])))
|
mu_xy = sparse.diags_array(numpy.hstack((mu_parts[0], mu_parts[1])))
|
||||||
mu_z_inv = sparse.diags(1 / mu_parts[2])
|
mu_z_inv = sparse.diags_array(1 / mu_parts[2])
|
||||||
|
|
||||||
op = (
|
op = (
|
||||||
omega * omega * eps_yx @ mu_xy
|
omega * omega * eps_yx @ mu_xy
|
||||||
@ -331,14 +331,14 @@ def operator_h(
|
|||||||
|
|
||||||
|
|
||||||
def normalized_fields_e(
|
def normalized_fields_e(
|
||||||
e_xy: ArrayLike,
|
e_xy: vcfdfield2,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> tuple[vcfdfield_t, vcfdfield_t]:
|
) -> tuple[vcfdslice_t, vcfdslice_t]:
|
||||||
"""
|
"""
|
||||||
Given a vector `e_xy` containing the vectorized E_x and E_y fields,
|
Given a vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
returns normalized, vectorized E and H fields for the system.
|
returns normalized, vectorized E and H fields for the system.
|
||||||
@ -366,14 +366,14 @@ def normalized_fields_e(
|
|||||||
|
|
||||||
|
|
||||||
def normalized_fields_h(
|
def normalized_fields_h(
|
||||||
h_xy: ArrayLike,
|
h_xy: vcfdfield2,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> tuple[vcfdfield_t, vcfdfield_t]:
|
) -> tuple[vcfdslice_t, vcfdslice_t]:
|
||||||
"""
|
"""
|
||||||
Given a vector `h_xy` containing the vectorized H_x and H_y fields,
|
Given a vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
returns normalized, vectorized E and H fields for the system.
|
returns normalized, vectorized E and H fields for the system.
|
||||||
@ -401,30 +401,22 @@ def normalized_fields_h(
|
|||||||
|
|
||||||
|
|
||||||
def _normalized_fields(
|
def _normalized_fields(
|
||||||
e: vcfdfield_t,
|
e: vcfdslice,
|
||||||
h: vcfdfield_t,
|
h: vcfdslice,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
prop_phase: float = 0,
|
prop_phase: float = 0,
|
||||||
) -> tuple[vcfdfield_t, vcfdfield_t]:
|
) -> tuple[vcfdslice_t, vcfdslice_t]:
|
||||||
# TODO documentation
|
# TODO documentation
|
||||||
shape = [s.size for s in dxes[0]]
|
shape = [s.size for s in dxes[0]]
|
||||||
dxes_real = [[numpy.real(d) for d in numpy.meshgrid(*dxes[v], indexing='ij')] for v in (0, 1)]
|
|
||||||
|
|
||||||
E = unvec(e, shape)
|
|
||||||
H = unvec(h, shape)
|
|
||||||
|
|
||||||
# Find time-averaged Sz and normalize to it
|
# Find time-averaged Sz and normalize to it
|
||||||
# H phase is adjusted by a half-cell forward shift for Yee cell, and 1-cell reverse shift for Poynting
|
Sz_tavg = inner_product(e, h, dxes=dxes, prop_phase=prop_phase, conj_h=True).real
|
||||||
phase = numpy.exp(-1j * -prop_phase / 2)
|
|
||||||
Sz_a = E[0] * numpy.conj(H[1] * phase) * dxes_real[0][1] * dxes_real[1][0]
|
|
||||||
Sz_b = E[1] * numpy.conj(H[0] * phase) * dxes_real[0][0] * dxes_real[1][1]
|
|
||||||
Sz_tavg = numpy.real(Sz_a.sum() - Sz_b.sum()) * 0.5 # 0.5 since E, H are assumed to be peak (not RMS) amplitudes
|
|
||||||
assert Sz_tavg > 0, f'Found a mode propagating in the wrong direction! {Sz_tavg=}'
|
assert Sz_tavg > 0, f'Found a mode propagating in the wrong direction! {Sz_tavg=}'
|
||||||
|
|
||||||
energy = epsilon * e.conj() * e
|
energy = numpy.real(epsilon * e.conj() * e)
|
||||||
|
|
||||||
norm_amplitude = 1 / numpy.sqrt(Sz_tavg)
|
norm_amplitude = 1 / numpy.sqrt(Sz_tavg)
|
||||||
norm_angle = -numpy.angle(e[energy.argmax()]) # Will randomly add a negative sign when mode is symmetric
|
norm_angle = -numpy.angle(e[energy.argmax()]) # Will randomly add a negative sign when mode is symmetric
|
||||||
@ -434,22 +426,23 @@ def _normalized_fields(
|
|||||||
sign = numpy.sign(E_weighted[:,
|
sign = numpy.sign(E_weighted[:,
|
||||||
:max(shape[0] // 2, 1),
|
:max(shape[0] // 2, 1),
|
||||||
:max(shape[1] // 2, 1)].real.sum())
|
:max(shape[1] // 2, 1)].real.sum())
|
||||||
|
assert sign != 0
|
||||||
|
|
||||||
norm_factor = sign * norm_amplitude * numpy.exp(1j * norm_angle)
|
norm_factor = sign * norm_amplitude * numpy.exp(1j * norm_angle)
|
||||||
|
|
||||||
e *= norm_factor
|
e *= norm_factor
|
||||||
h *= norm_factor
|
h *= norm_factor
|
||||||
|
|
||||||
return e, h
|
return vcfdslice_t(e), vcfdslice_t(h)
|
||||||
|
|
||||||
|
|
||||||
def exy2h(
|
def exy2h(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None
|
mu: vfdslice | None = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
into a vectorized H containing all three H components
|
into a vectorized H containing all three H components
|
||||||
@ -472,10 +465,10 @@ def exy2h(
|
|||||||
def hxy2e(
|
def hxy2e(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None
|
mu: vfdslice | None = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
into a vectorized E containing all three E components
|
into a vectorized E containing all three E components
|
||||||
@ -497,9 +490,9 @@ def hxy2e(
|
|||||||
|
|
||||||
def hxy2h(
|
def hxy2h(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
mu: vfdfield_t | None = None
|
mu: vfdslice | None = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
Operator which transforms the vector `h_xy` containing the vectorized H_x and H_y fields,
|
||||||
into a vectorized H containing all three H components
|
into a vectorized H containing all three H components
|
||||||
@ -518,26 +511,53 @@ def hxy2h(
|
|||||||
|
|
||||||
if mu is not None:
|
if mu is not None:
|
||||||
mu_parts = numpy.split(mu, 3)
|
mu_parts = numpy.split(mu, 3)
|
||||||
mu_xy = sparse.diags(numpy.hstack((mu_parts[0], mu_parts[1])))
|
mu_xy = sparse.diags_array(numpy.hstack((mu_parts[0], mu_parts[1])))
|
||||||
mu_z_inv = sparse.diags(1 / mu_parts[2])
|
mu_z_inv = sparse.diags_array(1 / mu_parts[2])
|
||||||
|
|
||||||
hxy2hz = mu_z_inv @ hxy2hz @ mu_xy
|
hxy2hz = mu_z_inv @ hxy2hz @ mu_xy
|
||||||
|
|
||||||
n_pts = dxes[1][0].size * dxes[1][1].size
|
n_pts = dxes[1][0].size * dxes[1][1].size
|
||||||
op = sparse.vstack((sparse.eye(2 * n_pts),
|
op = sparse.vstack((sparse.eye_array(2 * n_pts),
|
||||||
hxy2hz))
|
hxy2hz))
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def exy2e(
|
def exy2e(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
r"""
|
||||||
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
into a vectorized E containing all three E components
|
into a vectorized E containing all three E components
|
||||||
|
|
||||||
|
From the operator derivation (see module docs), we have
|
||||||
|
|
||||||
|
$$
|
||||||
|
\imath \omega \epsilon_{zz} E_z = \hat{\partial}_x H_y - \hat{\partial}_y H_x \\
|
||||||
|
$$
|
||||||
|
|
||||||
|
as well as the intermediate equations
|
||||||
|
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
\imath \beta H_y &= \imath \omega \epsilon_{xx} E_x - \hat{\partial}_y H_z \\
|
||||||
|
\imath \beta H_x &= -\imath \omega \epsilon_{yy} E_y - \hat{\partial}_x H_z \\
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
|
Combining these, we get
|
||||||
|
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
E_z &= \frac{1}{- \omega \beta \epsilon_{zz}} ((
|
||||||
|
\hat{\partial}_y \hat{\partial}_x H_z
|
||||||
|
-\hat{\partial}_x \hat{\partial}_y H_z)
|
||||||
|
+ \imath \omega (\hat{\partial}_x \epsilon_{xx} E_x + \hat{\partial}_y \epsilon{yy} E_y))
|
||||||
|
&= \frac{1}{\imath \beta \epsilon_{zz}} (\hat{\partial}_x \epsilon_{xx} E_x + \hat{\partial}_y \epsilon{yy} E_y)
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
It should satisfy `operator_e() @ e_xy == wavenumber**2 * e_xy`
|
||||||
@ -552,13 +572,13 @@ def exy2e(
|
|||||||
|
|
||||||
if epsilon is not None:
|
if epsilon is not None:
|
||||||
epsilon_parts = numpy.split(epsilon, 3)
|
epsilon_parts = numpy.split(epsilon, 3)
|
||||||
epsilon_xy = sparse.diags(numpy.hstack((epsilon_parts[0], epsilon_parts[1])))
|
epsilon_xy = sparse.diags_array(numpy.hstack((epsilon_parts[0], epsilon_parts[1])))
|
||||||
epsilon_z_inv = sparse.diags(1 / epsilon_parts[2])
|
epsilon_z_inv = sparse.diags_array(1 / epsilon_parts[2])
|
||||||
|
|
||||||
exy2ez = epsilon_z_inv @ exy2ez @ epsilon_xy
|
exy2ez = epsilon_z_inv @ exy2ez @ epsilon_xy
|
||||||
|
|
||||||
n_pts = dxes[0][0].size * dxes[0][1].size
|
n_pts = dxes[0][0].size * dxes[0][1].size
|
||||||
op = sparse.vstack((sparse.eye(2 * n_pts),
|
op = sparse.vstack((sparse.eye_array(2 * n_pts),
|
||||||
exy2ez))
|
exy2ez))
|
||||||
return op
|
return op
|
||||||
|
|
||||||
@ -566,12 +586,12 @@ def exy2e(
|
|||||||
def e2h(
|
def e2h(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
mu: vfdfield_t | None = None
|
mu: vfdslice | None = None
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Returns an operator which, when applied to a vectorized E eigenfield, produces
|
Returns an operator which, when applied to a vectorized E eigenfield, produces
|
||||||
the vectorized H eigenfield.
|
the vectorized H eigenfield slice.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
@ -584,19 +604,19 @@ def e2h(
|
|||||||
"""
|
"""
|
||||||
op = curl_e(wavenumber, dxes) / (-1j * omega)
|
op = curl_e(wavenumber, dxes) / (-1j * omega)
|
||||||
if mu is not None:
|
if mu is not None:
|
||||||
op = sparse.diags(1 / mu) @ op
|
op = sparse.diags_array(1 / mu) @ op
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def h2e(
|
def h2e(
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t
|
epsilon: vfdslice,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Returns an operator which, when applied to a vectorized H eigenfield, produces
|
Returns an operator which, when applied to a vectorized H eigenfield, produces
|
||||||
the vectorized E eigenfield.
|
the vectorized E eigenfield slice.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
@ -607,13 +627,13 @@ def h2e(
|
|||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representation of the operator.
|
Sparse matrix representation of the operator.
|
||||||
"""
|
"""
|
||||||
op = sparse.diags(1 / (1j * omega * epsilon)) @ curl_h(wavenumber, dxes)
|
op = sparse.diags_array(1 / (1j * omega * epsilon)) @ curl_h(wavenumber, dxes)
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def curl_e(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
def curl_e(wavenumber: complex, dxes: dx_lists2_t) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Discretized curl operator for use with the waveguide E field.
|
Discretized curl operator for use with the waveguide E field slice.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
@ -622,18 +642,18 @@ def curl_e(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representation of the operator.
|
Sparse matrix representation of the operator.
|
||||||
"""
|
"""
|
||||||
n = 1
|
nn = 1
|
||||||
for d in dxes[0]:
|
for dd in dxes[0]:
|
||||||
n *= len(d)
|
nn *= len(dd)
|
||||||
|
|
||||||
Bz = -1j * wavenumber * sparse.eye(n)
|
Bz = -1j * wavenumber * sparse.eye_array(nn)
|
||||||
Dfx, Dfy = deriv_forward(dxes[0])
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
return cross([Dfx, Dfy, Bz])
|
return cross([Dfx, Dfy, Bz])
|
||||||
|
|
||||||
|
|
||||||
def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
def curl_h(wavenumber: complex, dxes: dx_lists2_t) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Discretized curl operator for use with the waveguide H field.
|
Discretized curl operator for use with the waveguide H field slice.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
wavenumber: Wavenumber assuming fields have z-dependence of `exp(-i * wavenumber * z)`
|
||||||
@ -642,22 +662,22 @@ def curl_h(wavenumber: complex, dxes: dx_lists_t) -> sparse.spmatrix:
|
|||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representation of the operator.
|
Sparse matrix representation of the operator.
|
||||||
"""
|
"""
|
||||||
n = 1
|
nn = 1
|
||||||
for d in dxes[1]:
|
for dd in dxes[1]:
|
||||||
n *= len(d)
|
nn *= len(dd)
|
||||||
|
|
||||||
Bz = -1j * wavenumber * sparse.eye(n)
|
Bz = -1j * wavenumber * sparse.eye_array(nn)
|
||||||
Dbx, Dby = deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
return cross([Dbx, Dby, Bz])
|
return cross([Dbx, Dby, Bz])
|
||||||
|
|
||||||
|
|
||||||
def h_err(
|
def h_err(
|
||||||
h: vcfdfield_t,
|
h: vcfdslice,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None
|
mu: vfdslice | None = None
|
||||||
) -> float:
|
) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates the relative error in the H field
|
Calculates the relative error in the H field
|
||||||
@ -676,7 +696,7 @@ def h_err(
|
|||||||
ce = curl_e(wavenumber, dxes)
|
ce = curl_e(wavenumber, dxes)
|
||||||
ch = curl_h(wavenumber, dxes)
|
ch = curl_h(wavenumber, dxes)
|
||||||
|
|
||||||
eps_inv = sparse.diags(1 / epsilon)
|
eps_inv = sparse.diags_array(1 / epsilon)
|
||||||
|
|
||||||
if mu is None:
|
if mu is None:
|
||||||
op = ce @ eps_inv @ ch @ h - omega ** 2 * h
|
op = ce @ eps_inv @ ch @ h - omega ** 2 * h
|
||||||
@ -687,12 +707,12 @@ def h_err(
|
|||||||
|
|
||||||
|
|
||||||
def e_err(
|
def e_err(
|
||||||
e: vcfdfield_t,
|
e: vcfdslice,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
) -> float:
|
) -> float:
|
||||||
"""
|
"""
|
||||||
Calculates the relative error in the E field
|
Calculates the relative error in the E field
|
||||||
@ -714,21 +734,21 @@ def e_err(
|
|||||||
if mu is None:
|
if mu is None:
|
||||||
op = ch @ ce @ e - omega ** 2 * (epsilon * e)
|
op = ch @ ce @ e - omega ** 2 * (epsilon * e)
|
||||||
else:
|
else:
|
||||||
mu_inv = sparse.diags(1 / mu)
|
mu_inv = sparse.diags_array(1 / mu)
|
||||||
op = ch @ mu_inv @ ce @ e - omega ** 2 * (epsilon * e)
|
op = ch @ mu_inv @ ce @ e - omega ** 2 * (epsilon * e)
|
||||||
|
|
||||||
return float(norm(op) / norm(e))
|
return float(norm(op) / norm(e))
|
||||||
|
|
||||||
|
|
||||||
def sensitivity(
|
def sensitivity(
|
||||||
e_norm: vcfdfield_t,
|
e_norm: vcfdslice,
|
||||||
h_norm: vcfdfield_t,
|
h_norm: vcfdslice,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
) -> vcfdfield_t:
|
) -> vcfdslice_t:
|
||||||
r"""
|
r"""
|
||||||
Given a waveguide structure (`dxes`, `epsilon`, `mu`) and mode fields
|
Given a waveguide structure (`dxes`, `epsilon`, `mu`) and mode fields
|
||||||
(`e_norm`, `h_norm`, `wavenumber`, `omega`), calculates the sensitivity of the wavenumber
|
(`e_norm`, `h_norm`, `wavenumber`, `omega`), calculates the sensitivity of the wavenumber
|
||||||
@ -802,11 +822,11 @@ def sensitivity(
|
|||||||
Dbx, Dby = deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
|
||||||
eps_x, eps_y, eps_z = numpy.split(epsilon, 3)
|
eps_x, eps_y, eps_z = numpy.split(epsilon, 3)
|
||||||
eps_xy = sparse.diags(numpy.hstack((eps_x, eps_y)))
|
eps_xy = sparse.diags_array(numpy.hstack((eps_x, eps_y)))
|
||||||
eps_z_inv = sparse.diags(1 / eps_z)
|
eps_z_inv = sparse.diags_array(1 / eps_z)
|
||||||
|
|
||||||
mu_x, mu_y, _mu_z = numpy.split(mu, 3)
|
mu_x, mu_y, _mu_z = numpy.split(mu, 3)
|
||||||
mu_yx = sparse.diags(numpy.hstack((mu_y, mu_x)))
|
mu_yx = sparse.diags_array(numpy.hstack((mu_y, mu_x)))
|
||||||
|
|
||||||
da_exxhyy = vec(dxes[1][0][:, None] * dxes[0][1][None, :])
|
da_exxhyy = vec(dxes[1][0][:, None] * dxes[0][1][None, :])
|
||||||
da_eyyhxx = vec(dxes[1][1][None, :] * dxes[0][0][:, None])
|
da_eyyhxx = vec(dxes[1][1][None, :] * dxes[0][0][:, None])
|
||||||
@ -820,15 +840,15 @@ def sensitivity(
|
|||||||
norm = hv_yx_conj @ ev_xy
|
norm = hv_yx_conj @ ev_xy
|
||||||
|
|
||||||
sens_tot = numpy.concatenate([sens_xy1 + sens_xy2, sens_z]) / (2 * wavenumber * norm)
|
sens_tot = numpy.concatenate([sens_xy1 + sens_xy2, sens_z]) / (2 * wavenumber * norm)
|
||||||
return sens_tot
|
return vcfdslice_t(sens_tot)
|
||||||
|
|
||||||
|
|
||||||
def solve_modes(
|
def solve_modes(
|
||||||
mode_numbers: list[int],
|
mode_numbers: Sequence[int],
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
mu: vfdfield_t | None = None,
|
mu: vfdslice | None = None,
|
||||||
mode_margin: int = 2,
|
mode_margin: int = 2,
|
||||||
) -> tuple[NDArray[numpy.complex128], NDArray[numpy.complex128]]:
|
) -> tuple[NDArray[numpy.complex128], NDArray[numpy.complex128]]:
|
||||||
"""
|
"""
|
||||||
@ -845,32 +865,38 @@ def solve_modes(
|
|||||||
ability to find the correct mode. Default 2.
|
ability to find the correct mode. Default 2.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
e_xys: list of vfdfield_t specifying fields
|
e_xys: NDArray of vfdfield_t specifying fields. First dimension is mode number.
|
||||||
wavenumbers: list of wavenumbers
|
wavenumbers: list of wavenumbers
|
||||||
"""
|
"""
|
||||||
|
|
||||||
'''
|
#
|
||||||
Solve for the largest-magnitude eigenvalue of the real operator
|
# Solve for the largest-magnitude eigenvalue of the real operator
|
||||||
'''
|
#
|
||||||
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
||||||
mu_real = None if mu is None else numpy.real(mu)
|
mu_real = None if mu is None else numpy.real(mu)
|
||||||
A_r = operator_e(numpy.real(omega), dxes_real, numpy.real(epsilon), mu_real)
|
A_r = operator_e(numpy.real(omega), dxes_real, numpy.real(epsilon), mu_real)
|
||||||
|
|
||||||
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_numbers) + mode_margin)
|
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_numbers) + mode_margin)
|
||||||
e_xys = eigvecs[:, -(numpy.array(mode_numbers) + 1)]
|
keep_inds = -(numpy.array(mode_numbers) + 1)
|
||||||
|
e_xys = eigvecs[:, keep_inds].T
|
||||||
|
eigvals = eigvals[keep_inds]
|
||||||
|
|
||||||
'''
|
#
|
||||||
Now solve for the eigenvector of the full operator, using the real operator's
|
# Now solve for the eigenvector of the full operator, using the real operator's
|
||||||
eigenvector as an initial guess for Rayleigh quotient iteration.
|
# eigenvector as an initial guess for Rayleigh quotient iteration.
|
||||||
'''
|
#
|
||||||
A = operator_e(omega, dxes, epsilon, mu)
|
A = operator_e(omega, dxes, epsilon, mu)
|
||||||
for nn in range(len(mode_numbers)):
|
for nn in range(len(mode_numbers)):
|
||||||
eigvals[nn], e_xys[:, nn] = rayleigh_quotient_iteration(A, e_xys[:, nn])
|
eigvals[nn], e_xys[nn, :] = rayleigh_quotient_iteration(A, e_xys[nn, :])
|
||||||
|
|
||||||
# Calculate the wave-vector (force the real part to be positive)
|
# Calculate the wave-vector (force the real part to be positive)
|
||||||
wavenumbers = numpy.sqrt(eigvals)
|
wavenumbers = numpy.sqrt(eigvals)
|
||||||
wavenumbers *= numpy.sign(numpy.real(wavenumbers))
|
wavenumbers *= numpy.sign(numpy.real(wavenumbers))
|
||||||
|
|
||||||
|
order = wavenumbers.argsort()[::-1]
|
||||||
|
e_xys = e_xys[order]
|
||||||
|
wavenumbers = wavenumbers[order]
|
||||||
|
|
||||||
return e_xys, wavenumbers
|
return e_xys, wavenumbers
|
||||||
|
|
||||||
|
|
||||||
@ -878,7 +904,7 @@ def solve_mode(
|
|||||||
mode_number: int,
|
mode_number: int,
|
||||||
*args: Any,
|
*args: Any,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> tuple[vcfdfield_t, complex]:
|
) -> tuple[vcfdfield2_t, complex]:
|
||||||
"""
|
"""
|
||||||
Wrapper around `solve_modes()` that solves for a single mode.
|
Wrapper around `solve_modes()` that solves for a single mode.
|
||||||
|
|
||||||
@ -892,4 +918,38 @@ def solve_mode(
|
|||||||
"""
|
"""
|
||||||
kwargs['mode_numbers'] = [mode_number]
|
kwargs['mode_numbers'] = [mode_number]
|
||||||
e_xys, wavenumbers = solve_modes(*args, **kwargs)
|
e_xys, wavenumbers = solve_modes(*args, **kwargs)
|
||||||
return e_xys[:, 0], wavenumbers[0]
|
return vcfdfield2_t(e_xys[0]), wavenumbers[0]
|
||||||
|
|
||||||
|
|
||||||
|
def inner_product( # TODO documentation
|
||||||
|
e1: vcfdfield2,
|
||||||
|
h2: vcfdfield2,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
prop_phase: float = 0,
|
||||||
|
conj_h: bool = False,
|
||||||
|
trapezoid: bool = False,
|
||||||
|
) -> complex:
|
||||||
|
|
||||||
|
shape = [s.size for s in dxes[0]]
|
||||||
|
|
||||||
|
# H phase is adjusted by a half-cell forward shift for Yee cell, and 1-cell reverse shift for Poynting
|
||||||
|
phase = numpy.exp(-1j * -prop_phase / 2)
|
||||||
|
|
||||||
|
E1 = unvec(e1, shape)
|
||||||
|
H2 = unvec(h2, shape) * phase
|
||||||
|
|
||||||
|
if conj_h:
|
||||||
|
H2 = numpy.conj(H2)
|
||||||
|
|
||||||
|
# Find time-averaged Sz and normalize to it
|
||||||
|
dxes_real = [[numpy.real(dxyz) for dxyz in dxeh] for dxeh in dxes]
|
||||||
|
if trapezoid:
|
||||||
|
Sz_a = numpy.trapezoid(numpy.trapezoid(E1[0] * H2[1], numpy.cumsum(dxes_real[0][1])), numpy.cumsum(dxes_real[1][0]))
|
||||||
|
Sz_b = numpy.trapezoid(numpy.trapezoid(E1[1] * H2[0], numpy.cumsum(dxes_real[0][0])), numpy.cumsum(dxes_real[1][1]))
|
||||||
|
else:
|
||||||
|
Sz_a = E1[0] * H2[1] * dxes_real[1][0][:, None] * dxes_real[0][1][None, :]
|
||||||
|
Sz_b = E1[1] * H2[0] * dxes_real[0][0][:, None] * dxes_real[1][1][None, :]
|
||||||
|
Sz = 0.5 * (Sz_a.sum() - Sz_b.sum())
|
||||||
|
return Sz
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,13 @@ Tools for working with waveguide modes in 3D domains.
|
|||||||
This module relies heavily on `waveguide_2d` and mostly just transforms
|
This module relies heavily on `waveguide_2d` and mostly just transforms
|
||||||
its parameters into 2D equivalents and expands the results back into 3D.
|
its parameters into 2D equivalents and expands the results back into 3D.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Any
|
from typing import Any, cast
|
||||||
|
from collections.abc import Sequence
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
from numpy import complexfloating
|
||||||
|
|
||||||
from ..fdmath import vec, unvec, dx_lists_t, fdfield_t, cfdfield_t
|
from ..fdmath import vec, unvec, dx_lists_t, cfdfield_t, fdfield, cfdfield
|
||||||
from . import operators, waveguide_2d
|
from . import operators, waveguide_2d
|
||||||
|
|
||||||
|
|
||||||
@ -19,9 +21,9 @@ def solve_mode(
|
|||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: Sequence[slice],
|
slices: Sequence[slice],
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> dict[str, complex | NDArray[numpy.float_]]:
|
) -> dict[str, complex | NDArray[complexfloating]]:
|
||||||
"""
|
"""
|
||||||
Given a 3D grid, selects a slice from the grid and attempts to
|
Given a 3D grid, selects a slice from the grid and attempts to
|
||||||
solve for an eigenmode propagating through that slice.
|
solve for an eigenmode propagating through that slice.
|
||||||
@ -40,9 +42,10 @@ def solve_mode(
|
|||||||
Returns:
|
Returns:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
'E': list[NDArray[numpy.float_]],
|
'E': NDArray[complexfloating],
|
||||||
'H': list[NDArray[numpy.float_]],
|
'H': NDArray[complexfloating],
|
||||||
'wavenumber': complex,
|
'wavenumber': complex,
|
||||||
|
'wavenumber_2d': complex,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
@ -51,9 +54,9 @@ def solve_mode(
|
|||||||
|
|
||||||
slices = tuple(slices)
|
slices = tuple(slices)
|
||||||
|
|
||||||
'''
|
#
|
||||||
Solve the 2D problem in the specified plane
|
# Solve the 2D problem in the specified plane
|
||||||
'''
|
#
|
||||||
# Define rotation to set z as propagation direction
|
# Define rotation to set z as propagation direction
|
||||||
order = numpy.roll(range(3), 2 - axis)
|
order = numpy.roll(range(3), 2 - axis)
|
||||||
reverse_order = numpy.roll(range(3), axis - 2)
|
reverse_order = numpy.roll(range(3), axis - 2)
|
||||||
@ -71,9 +74,10 @@ def solve_mode(
|
|||||||
}
|
}
|
||||||
e_xy, wavenumber_2d = waveguide_2d.solve_mode(mode_number, **args_2d)
|
e_xy, wavenumber_2d = waveguide_2d.solve_mode(mode_number, **args_2d)
|
||||||
|
|
||||||
'''
|
#
|
||||||
Apply corrections and expand to 3D
|
# Apply corrections and expand to 3D
|
||||||
'''
|
#
|
||||||
|
|
||||||
# Correct wavenumber to account for numerical dispersion.
|
# Correct wavenumber to account for numerical dispersion.
|
||||||
wavenumber = 2 / dx_prop * numpy.arcsin(wavenumber_2d * dx_prop / 2)
|
wavenumber = 2 / dx_prop * numpy.arcsin(wavenumber_2d * dx_prop / 2)
|
||||||
|
|
||||||
@ -92,9 +96,10 @@ def solve_mode(
|
|||||||
# Expand E, H to full epsilon space we were given
|
# Expand E, H to full epsilon space we were given
|
||||||
E = numpy.zeros_like(epsilon, dtype=complex)
|
E = numpy.zeros_like(epsilon, dtype=complex)
|
||||||
H = numpy.zeros_like(epsilon, dtype=complex)
|
H = numpy.zeros_like(epsilon, dtype=complex)
|
||||||
for a, o in enumerate(reverse_order):
|
for aa, oo in enumerate(reverse_order):
|
||||||
E[(a, *slices)] = e[o][:, :, None].transpose(reverse_order)
|
iii = cast('tuple[slice | int]', (aa, *slices))
|
||||||
H[(a, *slices)] = h[o][:, :, None].transpose(reverse_order)
|
E[iii] = e[oo][:, :, None].transpose(reverse_order)
|
||||||
|
H[iii] = h[oo][:, :, None].transpose(reverse_order)
|
||||||
|
|
||||||
results = {
|
results = {
|
||||||
'wavenumber': wavenumber,
|
'wavenumber': wavenumber,
|
||||||
@ -106,15 +111,15 @@ def solve_mode(
|
|||||||
|
|
||||||
|
|
||||||
def compute_source(
|
def compute_source(
|
||||||
E: cfdfield_t,
|
E: cfdfield,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
omega: complex,
|
omega: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: Sequence[slice],
|
slices: Sequence[slice],
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
) -> cfdfield_t:
|
) -> cfdfield_t:
|
||||||
"""
|
"""
|
||||||
Given an eigenmode obtained by `solve_mode`, returns the current source distribution
|
Given an eigenmode obtained by `solve_mode`, returns the current source distribution
|
||||||
@ -148,35 +153,32 @@ def compute_source(
|
|||||||
|
|
||||||
masked_e2j = operators.e_boundary_source(mask=vec(mask), omega=omega, dxes=dxes, epsilon=vec(epsilon), mu=vec(mu))
|
masked_e2j = operators.e_boundary_source(mask=vec(mask), omega=omega, dxes=dxes, epsilon=vec(epsilon), mu=vec(mu))
|
||||||
J = unvec(masked_e2j @ vec(E_expanded), E.shape[1:])
|
J = unvec(masked_e2j @ vec(E_expanded), E.shape[1:])
|
||||||
return J
|
return cfdfield_t(J)
|
||||||
|
|
||||||
|
|
||||||
def compute_overlap_e(
|
def compute_overlap_e(
|
||||||
E: cfdfield_t,
|
E: cfdfield,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
polarity: int,
|
polarity: int,
|
||||||
slices: Sequence[slice],
|
slices: Sequence[slice],
|
||||||
) -> cfdfield_t: # TODO DOCS
|
) -> cfdfield_t:
|
||||||
"""
|
"""
|
||||||
Given an eigenmode obtained by `solve_mode`, calculates an overlap_e for the
|
Given an eigenmode obtained by `solve_mode`, calculates an overlap_e for the
|
||||||
mode orthogonality relation Integrate(((E x H_mode) + (E_mode x H)) dot dn)
|
mode orthogonality relation Integrate(((E x H_mode) + (E_mode x H)) dot dn)
|
||||||
[assumes reflection symmetry].
|
[assumes reflection symmetry].
|
||||||
|
|
||||||
TODO: add reference
|
TODO: add reference or derivation for compute_overlap_e
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
E: E-field of the mode
|
E: E-field of the mode
|
||||||
H: H-field of the mode (advanced by half of a Yee cell from E)
|
|
||||||
wavenumber: Wavenumber of the mode
|
wavenumber: Wavenumber of the mode
|
||||||
omega: Angular frequency of the simulation
|
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types`
|
||||||
axis: Propagation axis (0=x, 1=y, 2=z)
|
axis: Propagation axis (0=x, 1=y, 2=z)
|
||||||
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
polarity: Propagation direction (+1 for +ve, -1 for -ve)
|
||||||
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
slices: `epsilon[tuple(slices)]` is used to select the portion of the grid to use
|
||||||
as the waveguide cross-section. slices[axis] should select only one item.
|
as the waveguide cross-section. slices[axis] should select only one item.
|
||||||
mu: Magnetic permeability (default 1 everywhere)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
overlap_e such that `numpy.sum(overlap_e * other_e.conj())` computes the overlap integral
|
overlap_e such that `numpy.sum(overlap_e * other_e.conj())` computes the overlap integral
|
||||||
@ -195,12 +197,14 @@ def compute_overlap_e(
|
|||||||
Etgt = numpy.zeros_like(Ee)
|
Etgt = numpy.zeros_like(Ee)
|
||||||
Etgt[slices2] = Ee[slices2]
|
Etgt[slices2] = Ee[slices2]
|
||||||
|
|
||||||
Etgt /= (Etgt.conj() * Etgt).sum()
|
# note no sqrt() when normalizing below since we want to get 1.0 after overlapping with the
|
||||||
return Etgt
|
# original field, not the normalized one
|
||||||
|
Etgt /= (Etgt.conj() * Etgt).sum() # type: ignore
|
||||||
|
return cfdfield_t(Etgt)
|
||||||
|
|
||||||
|
|
||||||
def expand_e(
|
def expand_e(
|
||||||
E: cfdfield_t,
|
E: cfdfield,
|
||||||
wavenumber: complex,
|
wavenumber: complex,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
axis: int,
|
axis: int,
|
||||||
@ -245,4 +249,4 @@ def expand_e(
|
|||||||
slices_in = (slice(None), *slices)
|
slices_in = (slice(None), *slices)
|
||||||
|
|
||||||
E_expanded[slices_exp] = phase_E * numpy.array(E)[slices_in]
|
E_expanded[slices_exp] = phase_E * numpy.array(E)[slices_in]
|
||||||
return E_expanded
|
return cfdfield_t(E_expanded)
|
||||||
|
|||||||
@ -1,34 +1,102 @@
|
|||||||
"""
|
r"""
|
||||||
Operators and helper functions for cylindrical waveguides with unchanging cross-section.
|
Operators and helper functions for cylindrical waveguides with unchanging cross-section.
|
||||||
|
|
||||||
WORK IN PROGRESS, CURRENTLY BROKEN
|
Waveguide operator is derived according to 10.1364/OL.33.001848.
|
||||||
|
The curl equations in the complex coordinate system become
|
||||||
|
|
||||||
As the z-dependence is known, all the functions in this file assume a 2D grid
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
-\imath \omega \mu_{xx} H_x &= \tilde{\partial}_y E_z + \imath \beta frac{E_y}{\tilde{t}_x} \\
|
||||||
|
-\imath \omega \mu_{yy} H_y &= -\imath \beta E_x - \frac{1}{\hat{t}_x} \tilde{\partial}_x \tilde{t}_x E_z \\
|
||||||
|
-\imath \omega \mu_{zz} H_z &= \tilde{\partial}_x E_y - \tilde{\partial}_y E_x \\
|
||||||
|
\imath \omega \epsilon_{xx} E_x &= \hat{\partial}_y H_z + \imath \beta \frac{H_y}{\hat{T}} \\
|
||||||
|
\imath \omega \epsilon_{yy} E_y &= -\imath \beta H_x - \{1}{\tilde{t}_x} \hat{\partial}_x \hat{t}_x} H_z \\
|
||||||
|
\imath \omega \epsilon_{zz} E_z &= \hat{\partial}_x H_y - \hat{\partial}_y H_x \\
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
|
where $t_x = 1 + \frac{\Delta_{x, m}}{R_0}$ is the grid spacing adjusted by the nominal radius $R0$.
|
||||||
|
|
||||||
|
Rewrite the last three equations as
|
||||||
|
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
\imath \beta H_y &= \imath \omega \hat{t}_x \epsilon_{xx} E_x - \hat{t}_x \hat{\partial}_y H_z \\
|
||||||
|
\imath \beta H_x &= -\imath \omega \hat{t}_x \epsilon_{yy} E_y - \hat{t}_x \hat{\partial}_x H_z \\
|
||||||
|
\imath \omega E_z &= \frac{1}{\epsilon_{zz}} \hat{\partial}_x H_y - \frac{1}{\epsilon_{zz}} \hat{\partial}_y H_x \\
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
|
The derivation then follows the same steps as the straight waveguide, leading to the eigenvalue problem
|
||||||
|
|
||||||
|
$$
|
||||||
|
\beta^2 \begin{bmatrix} E_x \\
|
||||||
|
E_y \end{bmatrix} =
|
||||||
|
(\omega^2 \begin{bmatrix} T_b T_b \mu_{yy} \epsilon_{xx} & 0 \\
|
||||||
|
0 & T_a T_a \mu_{xx} \epsilon_{yy} \end{bmatrix} +
|
||||||
|
\begin{bmatrix} -T_b \mu_{yy} \hat{\partial}_y \\
|
||||||
|
T_a \mu_{xx} \hat{\partial}_x \end{bmatrix} T_b \mu_{zz}^{-1}
|
||||||
|
\begin{bmatrix} -\tilde{\partial}_y & \tilde{\partial}_x \end{bmatrix} +
|
||||||
|
\begin{bmatrix} \tilde{\partial}_x \\
|
||||||
|
\tilde{\partial}_y \end{bmatrix} T_a \epsilon_{zz}^{-1}
|
||||||
|
\begin{bmatrix} \hat{\partial}_x T_b \epsilon_{xx} & \hat{\partial}_y T_a \epsilon_{yy} \end{bmatrix})
|
||||||
|
\begin{bmatrix} E_x \\
|
||||||
|
E_y \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
|
which resembles the straight waveguide eigenproblem with additonal $T_a$ and $T_b$ terms. These
|
||||||
|
are diagonal matrices containing the $t_x$ values:
|
||||||
|
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
T_a &= 1 + \frac{\Delta_{x, m }}{R_0}
|
||||||
|
T_b &= 1 + \frac{\Delta_{x, m + \frac{1}{2} }}{R_0}
|
||||||
|
\end{aligned}
|
||||||
|
|
||||||
|
|
||||||
|
TODO: consider 10.1364/OE.20.021583 for an alternate approach
|
||||||
|
$$
|
||||||
|
|
||||||
|
As in the straight waveguide case, all the functions in this file assume a 2D grid
|
||||||
(i.e. `dxes = [[[dr_e_0, dx_e_1, ...], [dy_e_0, ...]], [[dr_h_0, ...], [dy_h_0, ...]]]`).
|
(i.e. `dxes = [[[dr_e_0, dx_e_1, ...], [dy_e_0, ...]], [[dr_h_0, ...], [dy_h_0, ...]]]`).
|
||||||
"""
|
"""
|
||||||
# TODO update module docs
|
from typing import Any, cast
|
||||||
|
from collections.abc import Sequence
|
||||||
|
import logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
import scipy.sparse as sparse # type: ignore
|
from numpy.typing import NDArray, ArrayLike
|
||||||
|
from scipy import sparse
|
||||||
|
|
||||||
from ..fdmath import vec, unvec, dx_lists_t, fdfield_t, vfdfield_t, cfdfield_t
|
from ..fdmath import vec, unvec, dx_lists2_t, vcfdslice_t, vcfdfield2_t, vfdslice, vcfdslice, vcfdfield2
|
||||||
from ..fdmath.operators import deriv_forward, deriv_back
|
from ..fdmath.operators import deriv_forward, deriv_back
|
||||||
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
from ..eigensolvers import signed_eigensolve, rayleigh_quotient_iteration
|
||||||
|
from . import waveguide_2d
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def cylindrical_operator(
|
def cylindrical_operator(
|
||||||
omega: complex,
|
omega: float,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
r0: float,
|
rmin: float,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
r"""
|
||||||
Cylindrical coordinate waveguide operator of the form
|
Cylindrical coordinate waveguide operator of the form
|
||||||
|
|
||||||
(NOTE: See 10.1364/OL.33.001848)
|
$$
|
||||||
TODO: consider 10.1364/OE.20.021583
|
(\omega^2 \begin{bmatrix} T_b T_b \mu_{yy} \epsilon_{xx} & 0 \\
|
||||||
|
0 & T_a T_a \mu_{xx} \epsilon_{yy} \end{bmatrix} +
|
||||||
TODO
|
\begin{bmatrix} -T_b \mu_{yy} \hat{\partial}_y \\
|
||||||
|
T_a \mu_{xx} \hat{\partial}_x \end{bmatrix} T_b \mu_{zz}^{-1}
|
||||||
|
\begin{bmatrix} -\tilde{\partial}_y & \tilde{\partial}_x \end{bmatrix} +
|
||||||
|
\begin{bmatrix} \tilde{\partial}_x \\
|
||||||
|
\tilde{\partial}_y \end{bmatrix} T_a \epsilon_{zz}^{-1}
|
||||||
|
\begin{bmatrix} \hat{\partial}_x T_b \epsilon_{xx} & \hat{\partial}_y T_a \epsilon_{yy} \end{bmatrix})
|
||||||
|
\begin{bmatrix} E_x \\
|
||||||
|
E_y \end{bmatrix}
|
||||||
|
$$
|
||||||
|
|
||||||
for use with a field vector of the form `[E_r, E_y]`.
|
for use with a field vector of the form `[E_r, E_y]`.
|
||||||
|
|
||||||
@ -38,12 +106,13 @@ def cylindrical_operator(
|
|||||||
which can then be solved for the eigenmodes of the system
|
which can then be solved for the eigenmodes of the system
|
||||||
(an `exp(-i * wavenumber * theta)` theta-dependence is assumed for the fields).
|
(an `exp(-i * wavenumber * theta)` theta-dependence is assumed for the fields).
|
||||||
|
|
||||||
|
(NOTE: See module docs and 10.1364/OL.33.001848)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
omega: The angular frequency of the system
|
omega: The angular frequency of the system
|
||||||
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
epsilon: Vectorized dielectric constant grid
|
epsilon: Vectorized dielectric constant grid
|
||||||
r0: Radius of curvature for the simulation. This should be the minimum value of
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
r within the simulation domain.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Sparse matrix representation of the operator
|
Sparse matrix representation of the operator
|
||||||
@ -52,46 +121,34 @@ def cylindrical_operator(
|
|||||||
Dfx, Dfy = deriv_forward(dxes[0])
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
Dbx, Dby = deriv_back(dxes[1])
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
|
||||||
rx = r0 + numpy.cumsum(dxes[0][0])
|
Ta, Tb = dxes2T(dxes=dxes, rmin=rmin)
|
||||||
ry = r0 + dxes[0][0] / 2.0 + numpy.cumsum(dxes[1][0])
|
|
||||||
tx = rx / r0
|
|
||||||
ty = ry / r0
|
|
||||||
|
|
||||||
Tx = sparse.diags(vec(tx[:, None].repeat(dxes[0][1].size, axis=1)))
|
|
||||||
Ty = sparse.diags(vec(ty[:, None].repeat(dxes[1][1].size, axis=1)))
|
|
||||||
|
|
||||||
eps_parts = numpy.split(epsilon, 3)
|
eps_parts = numpy.split(epsilon, 3)
|
||||||
eps_x = sparse.diags(eps_parts[0])
|
eps_x = sparse.diags_array(eps_parts[0])
|
||||||
eps_y = sparse.diags(eps_parts[1])
|
eps_y = sparse.diags_array(eps_parts[1])
|
||||||
eps_z_inv = sparse.diags(1 / eps_parts[2])
|
eps_z_inv = sparse.diags_array(1 / eps_parts[2])
|
||||||
|
|
||||||
pa = sparse.vstack((Dfx, Dfy)) @ Tx @ eps_z_inv @ sparse.hstack((Dbx, Dby))
|
|
||||||
pb = sparse.vstack((Dfx, Dfy)) @ Tx @ eps_z_inv @ sparse.hstack((Dby, Dbx))
|
|
||||||
a0 = Ty @ eps_x + omega**-2 * Dby @ Ty @ Dfy
|
|
||||||
a1 = Tx @ eps_y + omega**-2 * Dbx @ Ty @ Dfx
|
|
||||||
b0 = Dbx @ Ty @ Dfy
|
|
||||||
b1 = Dby @ Ty @ Dfx
|
|
||||||
|
|
||||||
diag = sparse.block_diag
|
|
||||||
|
|
||||||
omega2 = omega * omega
|
omega2 = omega * omega
|
||||||
|
diag = sparse.block_diag
|
||||||
|
|
||||||
op = (
|
sq0 = omega2 * diag((Tb @ Tb @ eps_x,
|
||||||
(omega2 * diag((Tx, Ty)) + pa) @ diag((a0, a1))
|
Ta @ Ta @ eps_y))
|
||||||
- (sparse.bmat(((None, Ty), (Tx, None))) + pb / omega2) @ diag((b0, b1))
|
lin0 = sparse.vstack((-Tb @ Dby, Ta @ Dbx)) @ Tb @ sparse.hstack((-Dfy, Dfx))
|
||||||
)
|
lin1 = sparse.vstack((Dfx, Dfy)) @ Ta @ eps_z_inv @ sparse.hstack((Dbx @ Tb @ eps_x,
|
||||||
|
Dby @ Ta @ eps_y))
|
||||||
|
op = sq0 + lin0 + lin1
|
||||||
return op
|
return op
|
||||||
|
|
||||||
|
|
||||||
def solve_mode(
|
def solve_modes(
|
||||||
mode_number: int,
|
mode_numbers: Sequence[int],
|
||||||
omega: complex,
|
omega: float,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists2_t,
|
||||||
epsilon: vfdfield_t,
|
epsilon: vfdslice,
|
||||||
r0: float,
|
rmin: float,
|
||||||
) -> dict[str, complex | cfdfield_t]:
|
mode_margin: int = 2,
|
||||||
|
) -> tuple[NDArray[numpy.complex128], NDArray[numpy.complex128]]:
|
||||||
"""
|
"""
|
||||||
TODO: fixup
|
|
||||||
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode
|
Given a 2d (r, y) slice of epsilon, attempts to solve for the eigenmode
|
||||||
of the bent waveguide with the specified mode number.
|
of the bent waveguide with the specified mode number.
|
||||||
|
|
||||||
@ -101,48 +158,336 @@ def solve_mode(
|
|||||||
dxes: Grid parameters [dx_e, dx_h] as described in meanas.fdmath.types.
|
dxes: Grid parameters [dx_e, dx_h] as described in meanas.fdmath.types.
|
||||||
The first coordinate is assumed to be r, the second is y.
|
The first coordinate is assumed to be r, the second is y.
|
||||||
epsilon: Dielectric constant
|
epsilon: Dielectric constant
|
||||||
r0: Radius of curvature for the simulation. This should be the minimum value of
|
rmin: Radius of curvature for the simulation. This should be the minimum value of
|
||||||
r within the simulation domain.
|
r within the simulation domain.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
```
|
e_xys: NDArray of vfdfield_t specifying fields. First dimension is mode number.
|
||||||
{
|
angular_wavenumbers: list of wavenumbers in 1/rad units.
|
||||||
'E': list[NDArray[numpy.complex_]],
|
|
||||||
'H': list[NDArray[numpy.complex_]],
|
|
||||||
'wavenumber': complex,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
'''
|
#
|
||||||
Solve for the largest-magnitude eigenvalue of the real operator
|
# Solve for the largest-magnitude eigenvalue of the real operator
|
||||||
'''
|
#
|
||||||
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
dxes_real = [[numpy.real(dx) for dx in dxi] for dxi in dxes]
|
||||||
|
|
||||||
A_r = cylindrical_operator(numpy.real(omega), dxes_real, numpy.real(epsilon), r0)
|
A_r = cylindrical_operator(numpy.real(omega), dxes_real, numpy.real(epsilon), rmin=rmin)
|
||||||
eigvals, eigvecs = signed_eigensolve(A_r, mode_number + 3)
|
eigvals, eigvecs = signed_eigensolve(A_r, max(mode_numbers) + mode_margin)
|
||||||
e_xy = eigvecs[:, -(mode_number + 1)]
|
keep_inds = -(numpy.array(mode_numbers) + 1)
|
||||||
|
e_xys = eigvecs[:, keep_inds].T
|
||||||
|
eigvals = eigvals[keep_inds]
|
||||||
|
|
||||||
'''
|
#
|
||||||
Now solve for the eigenvector of the full operator, using the real operator's
|
# Now solve for the eigenvector of the full operator, using the real operator's
|
||||||
eigenvector as an initial guess for Rayleigh quotient iteration.
|
# eigenvector as an initial guess for Rayleigh quotient iteration.
|
||||||
'''
|
#
|
||||||
A = cylindrical_operator(omega, dxes, epsilon, r0)
|
A = cylindrical_operator(omega, dxes, epsilon, rmin=rmin)
|
||||||
eigval, e_xy = rayleigh_quotient_iteration(A, e_xy)
|
for nn in range(len(mode_numbers)):
|
||||||
|
eigvals[nn], e_xys[nn, :] = rayleigh_quotient_iteration(A, e_xys[nn, :])
|
||||||
|
|
||||||
# Calculate the wave-vector (force the real part to be positive)
|
# Calculate the wave-vector (force the real part to be positive)
|
||||||
wavenumber = numpy.sqrt(eigval)
|
wavenumbers = numpy.sqrt(eigvals)
|
||||||
wavenumber *= numpy.sign(numpy.real(wavenumber))
|
wavenumbers *= numpy.sign(numpy.real(wavenumbers))
|
||||||
|
|
||||||
# TODO: Perform correction on wavenumber to account for numerical dispersion.
|
# Wavenumbers assume the mode is at rmin, which is unlikely
|
||||||
|
# Instead, return the wavenumber in inverse radians
|
||||||
|
angular_wavenumbers = wavenumbers * cast('complex', rmin)
|
||||||
|
|
||||||
shape = [d.size for d in dxes[0]]
|
order = angular_wavenumbers.argsort()[::-1]
|
||||||
e_xy = numpy.hstack((e_xy, numpy.zeros(shape[0] * shape[1])))
|
e_xys = e_xys[order]
|
||||||
fields = {
|
angular_wavenumbers = angular_wavenumbers[order]
|
||||||
'wavenumber': wavenumber,
|
|
||||||
'E': unvec(e_xy, shape),
|
|
||||||
# 'E': unvec(e, shape),
|
|
||||||
# 'H': unvec(h, shape),
|
|
||||||
}
|
|
||||||
|
|
||||||
return fields
|
return e_xys, angular_wavenumbers
|
||||||
|
|
||||||
|
|
||||||
|
def solve_mode(
|
||||||
|
mode_number: int,
|
||||||
|
*args: Any,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> tuple[vcfdslice, complex]:
|
||||||
|
"""
|
||||||
|
Wrapper around `solve_modes()` that solves for a single mode.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mode_number: 0-indexed mode number to solve for
|
||||||
|
*args: passed to `solve_modes()`
|
||||||
|
**kwargs: passed to `solve_modes()`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(e_xy, angular_wavenumber)
|
||||||
|
"""
|
||||||
|
kwargs['mode_numbers'] = [mode_number]
|
||||||
|
e_xys, angular_wavenumbers = solve_modes(*args, **kwargs)
|
||||||
|
return e_xys[0], angular_wavenumbers[0]
|
||||||
|
|
||||||
|
|
||||||
|
def linear_wavenumbers(
|
||||||
|
e_xys: list[vcfdfield2_t],
|
||||||
|
angular_wavenumbers: ArrayLike,
|
||||||
|
epsilon: vfdslice,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
) -> NDArray[numpy.complex128]:
|
||||||
|
"""
|
||||||
|
Calculate linear wavenumbers (1/distance) based on angular wavenumbers (1/rad)
|
||||||
|
and the mode's energy distribution.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
e_xys: Vectorized mode fields with shape (num_modes, 2 * x *y)
|
||||||
|
angular_wavenumbers: Wavenumbers assuming fields have theta-dependence of
|
||||||
|
`exp(-i * angular_wavenumber * theta)`. They should satisfy
|
||||||
|
`operator_e() @ e_xy == (angular_wavenumber / rmin) ** 2 * e_xy`
|
||||||
|
epsilon: Vectorized dielectric constant grid with shape (3, x, y)
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
NDArray containing the calculated linear (1/distance) wavenumbers
|
||||||
|
"""
|
||||||
|
angular_wavenumbers = numpy.asarray(angular_wavenumbers)
|
||||||
|
mode_radii = numpy.empty_like(angular_wavenumbers, dtype=float)
|
||||||
|
|
||||||
|
shape2d = (len(dxes[0][0]), len(dxes[0][1]))
|
||||||
|
epsilon2d = unvec(epsilon, shape2d)[:2]
|
||||||
|
grid_radii = rmin + numpy.cumsum(dxes[0][0])
|
||||||
|
for ii in range(angular_wavenumbers.size):
|
||||||
|
efield = unvec(e_xys[ii], shape2d, 2)
|
||||||
|
energy = numpy.real((efield * efield.conj()) * epsilon2d)
|
||||||
|
energy_vs_x = energy.sum(axis=(0, 2))
|
||||||
|
mode_radii[ii] = (grid_radii * energy_vs_x).sum() / energy_vs_x.sum()
|
||||||
|
|
||||||
|
logger.info(f'{mode_radii=}')
|
||||||
|
lin_wavenumbers = angular_wavenumbers / mode_radii
|
||||||
|
return lin_wavenumbers
|
||||||
|
|
||||||
|
|
||||||
|
def exy2h(
|
||||||
|
angular_wavenumber: complex,
|
||||||
|
omega: float,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
epsilon: vfdslice,
|
||||||
|
mu: vfdslice | None = None
|
||||||
|
) -> sparse.sparray:
|
||||||
|
"""
|
||||||
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
|
into a vectorized H containing all three H components
|
||||||
|
|
||||||
|
Args:
|
||||||
|
angular_wavenumber: Wavenumber assuming fields have theta-dependence of
|
||||||
|
`exp(-i * angular_wavenumber * theta)`. It should satisfy
|
||||||
|
`operator_e() @ e_xy == (angular_wavenumber / rmin) ** 2 * e_xy`
|
||||||
|
omega: The angular frequency of the system
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
epsilon: Vectorized dielectric constant grid
|
||||||
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sparse matrix representing the operator.
|
||||||
|
"""
|
||||||
|
e2hop = e2h(angular_wavenumber=angular_wavenumber, omega=omega, dxes=dxes, rmin=rmin, mu=mu)
|
||||||
|
return e2hop @ exy2e(angular_wavenumber=angular_wavenumber, omega=omega, dxes=dxes, rmin=rmin, epsilon=epsilon)
|
||||||
|
|
||||||
|
|
||||||
|
def exy2e(
|
||||||
|
angular_wavenumber: complex,
|
||||||
|
omega: float,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
epsilon: vfdslice,
|
||||||
|
) -> sparse.sparray:
|
||||||
|
"""
|
||||||
|
Operator which transforms the vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
|
into a vectorized E containing all three E components
|
||||||
|
|
||||||
|
Unlike the straight waveguide case, the H_z components do not cancel and must be calculated
|
||||||
|
from E_x and E_y in order to then calculate E_z.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
angular_wavenumber: Wavenumber assuming fields have theta-dependence of
|
||||||
|
`exp(-i * angular_wavenumber * theta)`. It should satisfy
|
||||||
|
`operator_e() @ e_xy == (angular_wavenumber / rmin) ** 2 * e_xy`
|
||||||
|
omega: The angular frequency of the system
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
epsilon: Vectorized dielectric constant grid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sparse matrix representing the operator.
|
||||||
|
"""
|
||||||
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
|
Dbx, Dby = deriv_back(dxes[1])
|
||||||
|
wavenumber = angular_wavenumber / rmin
|
||||||
|
|
||||||
|
Ta, Tb = dxes2T(dxes=dxes, rmin=rmin)
|
||||||
|
Tai = sparse.diags_array(1 / Ta.diagonal())
|
||||||
|
#Tbi = sparse.diags_array(1 / Tb.diagonal())
|
||||||
|
|
||||||
|
epsilon_parts = numpy.split(epsilon, 3)
|
||||||
|
epsilon_x, epsilon_y = (sparse.diags_array(epsi) for epsi in epsilon_parts[:2])
|
||||||
|
epsilon_z_inv = sparse.diags_array(1 / epsilon_parts[2])
|
||||||
|
|
||||||
|
n_pts = dxes[0][0].size * dxes[0][1].size
|
||||||
|
zeros = sparse.coo_array((n_pts, n_pts))
|
||||||
|
|
||||||
|
mu_z = numpy.ones(n_pts)
|
||||||
|
mu_z_inv = sparse.diags_array(1 / mu_z)
|
||||||
|
exy2hz = 1 / (-1j * omega) * mu_z_inv @ sparse.hstack((Dfy, -Dfx))
|
||||||
|
hxy2ez = 1 / (1j * omega) * epsilon_z_inv @ sparse.hstack((Dby, -Dbx))
|
||||||
|
|
||||||
|
exy2hy = Tb / (1j * wavenumber) @ (-1j * omega * sparse.hstack((epsilon_x, zeros)) - Dby @ exy2hz)
|
||||||
|
exy2hx = Tb / (1j * wavenumber) @ ( 1j * omega * sparse.hstack((zeros, epsilon_y)) - Tai @ Dbx @ Tb @ exy2hz)
|
||||||
|
|
||||||
|
exy2ez = hxy2ez @ sparse.vstack((exy2hx, exy2hy))
|
||||||
|
|
||||||
|
op = sparse.vstack((sparse.eye_array(2 * n_pts),
|
||||||
|
exy2ez))
|
||||||
|
return op
|
||||||
|
|
||||||
|
|
||||||
|
def e2h(
|
||||||
|
angular_wavenumber: complex,
|
||||||
|
omega: float,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
mu: vfdslice | None = None
|
||||||
|
) -> sparse.sparray:
|
||||||
|
r"""
|
||||||
|
Returns an operator which, when applied to a vectorized E eigenfield, produces
|
||||||
|
the vectorized H eigenfield.
|
||||||
|
|
||||||
|
This operator is created directly from the initial coordinate-transformed equations:
|
||||||
|
$$
|
||||||
|
\begin{aligned}
|
||||||
|
\imath \omega \epsilon_{xx} E_x &= \hat{\partial}_y H_z + \imath \beta \frac{H_y}{\hat{T}} \\
|
||||||
|
\imath \omega \epsilon_{yy} E_y &= -\imath \beta H_x - \{1}{\tilde{t}_x} \hat{\partial}_x \hat{t}_x} H_z \\
|
||||||
|
\imath \omega \epsilon_{zz} E_z &= \hat{\partial}_x H_y - \hat{\partial}_y H_x \\
|
||||||
|
\end{aligned}
|
||||||
|
$$
|
||||||
|
|
||||||
|
Args:
|
||||||
|
angular_wavenumber: Wavenumber assuming fields have theta-dependence of
|
||||||
|
`exp(-i * angular_wavenumber * theta)`. It should satisfy
|
||||||
|
`operator_e() @ e_xy == (angular_wavenumber / rmin) ** 2 * e_xy`
|
||||||
|
omega: The angular frequency of the system
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sparse matrix representation of the operator.
|
||||||
|
"""
|
||||||
|
Dfx, Dfy = deriv_forward(dxes[0])
|
||||||
|
Ta, Tb = dxes2T(dxes=dxes, rmin=rmin)
|
||||||
|
Tai = sparse.diags_array(1 / Ta.diagonal())
|
||||||
|
Tbi = sparse.diags_array(1 / Tb.diagonal())
|
||||||
|
|
||||||
|
jB = 1j * angular_wavenumber / rmin
|
||||||
|
op = sparse.block_array([[ None, -jB * Tai, -Dfy],
|
||||||
|
[jB * Tbi, None, Tbi @ Dfx @ Ta],
|
||||||
|
[ Dfy, -Dfx, None]]) / (-1j * omega)
|
||||||
|
if mu is not None:
|
||||||
|
op = sparse.diags_array(1 / mu) @ op
|
||||||
|
return op
|
||||||
|
|
||||||
|
|
||||||
|
def dxes2T(
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
) -> tuple[NDArray[numpy.float64], NDArray[numpy.float64]]:
|
||||||
|
r"""
|
||||||
|
Returns the $T_a$ and $T_b$ diagonal matrices which are used to apply the cylindrical
|
||||||
|
coordinate transformation in various operators.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Sparse matrix representations of the operators Ta and Tb
|
||||||
|
"""
|
||||||
|
ra = rmin + numpy.cumsum(dxes[0][0]) # Radius at Ey points
|
||||||
|
rb = rmin + dxes[0][0] / 2.0 + numpy.cumsum(dxes[1][0]) # Radius at Ex points
|
||||||
|
ta = ra / rmin
|
||||||
|
tb = rb / rmin
|
||||||
|
|
||||||
|
Ta = sparse.diags_array(vec(ta[:, None].repeat(dxes[0][1].size, axis=1)))
|
||||||
|
Tb = sparse.diags_array(vec(tb[:, None].repeat(dxes[1][1].size, axis=1)))
|
||||||
|
return Ta, Tb
|
||||||
|
|
||||||
|
|
||||||
|
def normalized_fields_e(
|
||||||
|
e_xy: vcfdfield2,
|
||||||
|
angular_wavenumber: complex,
|
||||||
|
omega: float,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float,
|
||||||
|
epsilon: vfdslice,
|
||||||
|
mu: vfdslice | None = None,
|
||||||
|
prop_phase: float = 0,
|
||||||
|
) -> tuple[vcfdslice_t, vcfdslice_t]:
|
||||||
|
"""
|
||||||
|
Given a vector `e_xy` containing the vectorized E_x and E_y fields,
|
||||||
|
returns normalized, vectorized E and H fields for the system.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
e_xy: Vector containing E_x and E_y fields
|
||||||
|
angular_wavenumber: Wavenumber assuming fields have theta-dependence of
|
||||||
|
`exp(-i * angular_wavenumber * theta)`. It should satisfy
|
||||||
|
`operator_e() @ e_xy == (angular_wavenumber / rmin) ** 2 * e_xy`
|
||||||
|
omega: The angular frequency of the system
|
||||||
|
dxes: Grid parameters `[dx_e, dx_h]` as described in `meanas.fdmath.types` (2D)
|
||||||
|
rmin: Radius at the left edge of the simulation domain (at minimum 'x')
|
||||||
|
epsilon: Vectorized dielectric constant grid
|
||||||
|
mu: Vectorized magnetic permeability grid (default 1 everywhere)
|
||||||
|
prop_phase: Phase shift `(dz * corrected_wavenumber)` over 1 cell in propagation direction.
|
||||||
|
Default 0 (continuous propagation direction, i.e. dz->0).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
`(e, h)`, where each field is vectorized, normalized,
|
||||||
|
and contains all three vector components.
|
||||||
|
"""
|
||||||
|
e = exy2e(angular_wavenumber=angular_wavenumber, omega=omega, dxes=dxes, rmin=rmin, epsilon=epsilon) @ e_xy
|
||||||
|
h = exy2h(angular_wavenumber=angular_wavenumber, omega=omega, dxes=dxes, rmin=rmin, epsilon=epsilon, mu=mu) @ e_xy
|
||||||
|
e_norm, h_norm = _normalized_fields(e=e, h=h, omega=omega, dxes=dxes, rmin=rmin, epsilon=epsilon,
|
||||||
|
mu=mu, prop_phase=prop_phase)
|
||||||
|
return e_norm, h_norm
|
||||||
|
|
||||||
|
|
||||||
|
def _normalized_fields(
|
||||||
|
e: vcfdslice,
|
||||||
|
h: vcfdslice,
|
||||||
|
omega: complex,
|
||||||
|
dxes: dx_lists2_t,
|
||||||
|
rmin: float, # Currently unused, but may want to use cylindrical poynting
|
||||||
|
epsilon: vfdslice,
|
||||||
|
mu: vfdslice | None = None,
|
||||||
|
prop_phase: float = 0,
|
||||||
|
) -> tuple[vcfdslice_t, vcfdslice_t]:
|
||||||
|
h *= -1
|
||||||
|
# TODO documentation for normalized_fields
|
||||||
|
shape = [s.size for s in dxes[0]]
|
||||||
|
|
||||||
|
# Find time-averaged Sz and normalize to it
|
||||||
|
# H phase is adjusted by a half-cell forward shift for Yee cell, and 1-cell reverse shift for Poynting
|
||||||
|
Sz_tavg = waveguide_2d.inner_product(e, h, dxes=dxes, prop_phase=prop_phase, conj_h=True).real # Note, using linear poynting vector
|
||||||
|
assert Sz_tavg > 0, f'Found a mode propagating in the wrong direction! {Sz_tavg=}'
|
||||||
|
|
||||||
|
energy = numpy.real(epsilon * e.conj() * e)
|
||||||
|
|
||||||
|
norm_amplitude = 1 / numpy.sqrt(Sz_tavg)
|
||||||
|
norm_angle = -numpy.angle(e[energy.argmax()]) # Will randomly add a negative sign when mode is symmetric
|
||||||
|
|
||||||
|
# Try to break symmetry to assign a consistent sign [experimental]
|
||||||
|
E_weighted = unvec(e * energy * numpy.exp(1j * norm_angle), shape)
|
||||||
|
sign = numpy.sign(E_weighted[:,
|
||||||
|
:max(shape[0] // 2, 1),
|
||||||
|
:max(shape[1] // 2, 1)].real.sum())
|
||||||
|
assert sign != 0
|
||||||
|
|
||||||
|
norm_factor = sign * norm_amplitude * numpy.exp(1j * norm_angle)
|
||||||
|
|
||||||
|
e *= norm_factor
|
||||||
|
h *= norm_factor
|
||||||
|
return vcfdslice_t(e), vcfdslice_t(h)
|
||||||
|
|||||||
@ -741,8 +741,46 @@ the true values can be multiplied back in after the simulation is complete if no
|
|||||||
normalized results are needed.
|
normalized results are needed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .types import fdfield_t, vfdfield_t, cfdfield_t, vcfdfield_t, dx_lists_t, dx_lists_mut
|
from .types import (
|
||||||
from .types import fdfield_updater_t, cfdfield_updater_t
|
fdfield_t as fdfield_t,
|
||||||
from .vectorization import vec, unvec
|
vfdfield_t as vfdfield_t,
|
||||||
from . import operators, functional, types, vectorization
|
cfdfield_t as cfdfield_t,
|
||||||
|
vcfdfield_t as vcfdfield_t,
|
||||||
|
fdfield2_t as fdfield2_t,
|
||||||
|
vfdfield2_t as vfdfield2_t,
|
||||||
|
cfdfield2_t as cfdfield2_t,
|
||||||
|
vcfdfield2_t as vcfdfield2_t,
|
||||||
|
fdfield as fdfield,
|
||||||
|
vfdfield as vfdfield,
|
||||||
|
cfdfield as cfdfield,
|
||||||
|
vcfdfield as vcfdfield,
|
||||||
|
fdfield2 as fdfield2,
|
||||||
|
vfdfield2 as vfdfield2,
|
||||||
|
cfdfield2 as cfdfield2,
|
||||||
|
vcfdfield2 as vcfdfield2,
|
||||||
|
fdslice_t as fdslice_t,
|
||||||
|
vfdslice_t as vfdslice_t,
|
||||||
|
cfdslice_t as cfdslice_t,
|
||||||
|
vcfdslice_t as vcfdslice_t,
|
||||||
|
fdslice as fdslice,
|
||||||
|
vfdslice as vfdslice,
|
||||||
|
cfdslice as cfdslice,
|
||||||
|
vcfdslice as vcfdslice,
|
||||||
|
dx_lists_t as dx_lists_t,
|
||||||
|
dx_lists2_t as dx_lists2_t,
|
||||||
|
dx_lists_mut as dx_lists_mut,
|
||||||
|
dx_lists2_mut as dx_lists2_mut,
|
||||||
|
fdfield_updater_t as fdfield_updater_t,
|
||||||
|
cfdfield_updater_t as cfdfield_updater_t,
|
||||||
|
)
|
||||||
|
from .vectorization import (
|
||||||
|
vec as vec,
|
||||||
|
unvec as unvec,
|
||||||
|
)
|
||||||
|
from . import (
|
||||||
|
operators as operators,
|
||||||
|
functional as functional,
|
||||||
|
types as types,
|
||||||
|
vectorization as vectorization,
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,18 @@ Math functions for finite difference simulations
|
|||||||
|
|
||||||
Basic discrete calculus etc.
|
Basic discrete calculus etc.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Callable
|
from typing import TypeVar
|
||||||
|
from collections.abc import Sequence, Callable
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
from numpy import floating, complexfloating
|
||||||
|
|
||||||
from .types import fdfield_t, fdfield_updater_t
|
from .types import fdfield_t, fdfield_updater_t
|
||||||
|
|
||||||
|
|
||||||
def deriv_forward(
|
def deriv_forward(
|
||||||
dx_e: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_e: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]:
|
) -> tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (backward variant).
|
Utility operators for taking discretized derivatives (backward variant).
|
||||||
@ -36,7 +38,7 @@ def deriv_forward(
|
|||||||
|
|
||||||
|
|
||||||
def deriv_back(
|
def deriv_back(
|
||||||
dx_h: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_h: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]:
|
) -> tuple[fdfield_updater_t, fdfield_updater_t, fdfield_updater_t]:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (forward variant).
|
Utility operators for taking discretized derivatives (forward variant).
|
||||||
@ -59,9 +61,12 @@ def deriv_back(
|
|||||||
return derivs
|
return derivs
|
||||||
|
|
||||||
|
|
||||||
|
TT = TypeVar('TT', bound='NDArray[floating | complexfloating]')
|
||||||
|
|
||||||
|
|
||||||
def curl_forward(
|
def curl_forward(
|
||||||
dx_e: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_e: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> fdfield_updater_t:
|
) -> Callable[[TT], TT]:
|
||||||
r"""
|
r"""
|
||||||
Curl operator for use with the E field.
|
Curl operator for use with the E field.
|
||||||
|
|
||||||
@ -75,7 +80,7 @@ def curl_forward(
|
|||||||
"""
|
"""
|
||||||
Dx, Dy, Dz = deriv_forward(dx_e)
|
Dx, Dy, Dz = deriv_forward(dx_e)
|
||||||
|
|
||||||
def ce_fun(e: fdfield_t) -> fdfield_t:
|
def ce_fun(e: TT) -> TT:
|
||||||
output = numpy.empty_like(e)
|
output = numpy.empty_like(e)
|
||||||
output[0] = Dy(e[2])
|
output[0] = Dy(e[2])
|
||||||
output[1] = Dz(e[0])
|
output[1] = Dz(e[0])
|
||||||
@ -89,8 +94,8 @@ def curl_forward(
|
|||||||
|
|
||||||
|
|
||||||
def curl_back(
|
def curl_back(
|
||||||
dx_h: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_h: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> fdfield_updater_t:
|
) -> Callable[[TT], TT]:
|
||||||
r"""
|
r"""
|
||||||
Create a function which takes the backward curl of a field.
|
Create a function which takes the backward curl of a field.
|
||||||
|
|
||||||
@ -104,7 +109,7 @@ def curl_back(
|
|||||||
"""
|
"""
|
||||||
Dx, Dy, Dz = deriv_back(dx_h)
|
Dx, Dy, Dz = deriv_back(dx_h)
|
||||||
|
|
||||||
def ch_fun(h: fdfield_t) -> fdfield_t:
|
def ch_fun(h: TT) -> TT:
|
||||||
output = numpy.empty_like(h)
|
output = numpy.empty_like(h)
|
||||||
output[0] = Dy(h[2])
|
output[0] = Dy(h[2])
|
||||||
output[1] = Dz(h[0])
|
output[1] = Dz(h[0])
|
||||||
@ -118,7 +123,7 @@ def curl_back(
|
|||||||
|
|
||||||
|
|
||||||
def curl_forward_parts(
|
def curl_forward_parts(
|
||||||
dx_e: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_e: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
Dx, Dy, Dz = deriv_forward(dx_e)
|
Dx, Dy, Dz = deriv_forward(dx_e)
|
||||||
|
|
||||||
@ -131,7 +136,7 @@ def curl_forward_parts(
|
|||||||
|
|
||||||
|
|
||||||
def curl_back_parts(
|
def curl_back_parts(
|
||||||
dx_h: Sequence[NDArray[numpy.float_]] | None = None,
|
dx_h: Sequence[NDArray[floating | complexfloating]] | None = None,
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
Dx, Dy, Dz = deriv_back(dx_h)
|
Dx, Dy, Dz = deriv_back(dx_h)
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,11 @@ Matrix operators for finite difference simulations
|
|||||||
|
|
||||||
Basic discrete calculus etc.
|
Basic discrete calculus etc.
|
||||||
"""
|
"""
|
||||||
from typing import Sequence
|
from collections.abc import Sequence
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
import scipy.sparse as sparse # type: ignore
|
from numpy import floating, complexfloating
|
||||||
|
from scipy import sparse
|
||||||
|
|
||||||
from .types import vfdfield_t
|
from .types import vfdfield_t
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ def shift_circ(
|
|||||||
axis: int,
|
axis: int,
|
||||||
shape: Sequence[int],
|
shape: Sequence[int],
|
||||||
shift_distance: int = 1,
|
shift_distance: int = 1,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Utility operator for performing a circular shift along a specified axis by a
|
Utility operator for performing a circular shift along a specified axis by a
|
||||||
specified number of elements.
|
specified number of elements.
|
||||||
@ -33,8 +34,8 @@ def shift_circ(
|
|||||||
if axis not in range(len(shape)):
|
if axis not in range(len(shape)):
|
||||||
raise Exception(f'Invalid direction: {axis}, shape is {shape}')
|
raise Exception(f'Invalid direction: {axis}, shape is {shape}')
|
||||||
|
|
||||||
shifts = [abs(shift_distance) if a == axis else 0 for a in range(3)]
|
shifts = [abs(shift_distance) if a == axis else 0 for a in range(len(shape))]
|
||||||
shifted_diags = [(numpy.arange(n) + s) % n for n, s in zip(shape, shifts)]
|
shifted_diags = [(numpy.arange(n) + s) % n for n, s in zip(shape, shifts, strict=True)]
|
||||||
ijk = numpy.meshgrid(*shifted_diags, indexing='ij')
|
ijk = numpy.meshgrid(*shifted_diags, indexing='ij')
|
||||||
|
|
||||||
n = numpy.prod(shape)
|
n = numpy.prod(shape)
|
||||||
@ -43,7 +44,7 @@ def shift_circ(
|
|||||||
|
|
||||||
vij = (numpy.ones(n), (i_ind, j_ind.ravel(order='C')))
|
vij = (numpy.ones(n), (i_ind, j_ind.ravel(order='C')))
|
||||||
|
|
||||||
d = sparse.csr_matrix(vij, shape=(n, n))
|
d = sparse.csr_array(vij, shape=(n, n))
|
||||||
|
|
||||||
if shift_distance < 0:
|
if shift_distance < 0:
|
||||||
d = d.T
|
d = d.T
|
||||||
@ -55,7 +56,7 @@ def shift_with_mirror(
|
|||||||
axis: int,
|
axis: int,
|
||||||
shape: Sequence[int],
|
shape: Sequence[int],
|
||||||
shift_distance: int = 1,
|
shift_distance: int = 1,
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Utility operator for performing an n-element shift along a specified axis, with mirror
|
Utility operator for performing an n-element shift along a specified axis, with mirror
|
||||||
boundary conditions applied to the cells beyond the receding edge.
|
boundary conditions applied to the cells beyond the receding edge.
|
||||||
@ -81,8 +82,8 @@ def shift_with_mirror(
|
|||||||
v = numpy.where(v < 0, - 1 - v, v)
|
v = numpy.where(v < 0, - 1 - v, v)
|
||||||
return v
|
return v
|
||||||
|
|
||||||
shifts = [shift_distance if a == axis else 0 for a in range(3)]
|
shifts = [shift_distance if a == axis else 0 for a in range(len(shape))]
|
||||||
shifted_diags = [mirrored_range(n, s) for n, s in zip(shape, shifts)]
|
shifted_diags = [mirrored_range(n, s) for n, s in zip(shape, shifts, strict=True)]
|
||||||
ijk = numpy.meshgrid(*shifted_diags, indexing='ij')
|
ijk = numpy.meshgrid(*shifted_diags, indexing='ij')
|
||||||
|
|
||||||
n = numpy.prod(shape)
|
n = numpy.prod(shape)
|
||||||
@ -91,13 +92,13 @@ def shift_with_mirror(
|
|||||||
|
|
||||||
vij = (numpy.ones(n), (i_ind, j_ind.ravel(order='C')))
|
vij = (numpy.ones(n), (i_ind, j_ind.ravel(order='C')))
|
||||||
|
|
||||||
d = sparse.csr_matrix(vij, shape=(n, n))
|
d = sparse.csr_array(vij, shape=(n, n))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def deriv_forward(
|
def deriv_forward(
|
||||||
dx_e: Sequence[NDArray[numpy.float_]],
|
dx_e: Sequence[NDArray[floating | complexfloating]],
|
||||||
) -> list[sparse.spmatrix]:
|
) -> list[sparse.sparray]:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (forward variant).
|
Utility operators for taking discretized derivatives (forward variant).
|
||||||
|
|
||||||
@ -113,18 +114,18 @@ def deriv_forward(
|
|||||||
|
|
||||||
dx_e_expanded = numpy.meshgrid(*dx_e, indexing='ij')
|
dx_e_expanded = numpy.meshgrid(*dx_e, indexing='ij')
|
||||||
|
|
||||||
def deriv(axis: int) -> sparse.spmatrix:
|
def deriv(axis: int) -> sparse.sparray:
|
||||||
return shift_circ(axis, shape, 1) - sparse.eye(n)
|
return shift_circ(axis, shape, 1) - sparse.eye_array(n)
|
||||||
|
|
||||||
Ds = [sparse.diags(+1 / dx.ravel(order='C')) @ deriv(a)
|
Ds = [sparse.diags_array(+1 / dx.ravel(order='C')) @ deriv(a)
|
||||||
for a, dx in enumerate(dx_e_expanded)]
|
for a, dx in enumerate(dx_e_expanded)]
|
||||||
|
|
||||||
return Ds
|
return Ds
|
||||||
|
|
||||||
|
|
||||||
def deriv_back(
|
def deriv_back(
|
||||||
dx_h: Sequence[NDArray[numpy.float_]],
|
dx_h: Sequence[NDArray[floating | complexfloating]],
|
||||||
) -> list[sparse.spmatrix]:
|
) -> list[sparse.sparray]:
|
||||||
"""
|
"""
|
||||||
Utility operators for taking discretized derivatives (backward variant).
|
Utility operators for taking discretized derivatives (backward variant).
|
||||||
|
|
||||||
@ -140,18 +141,18 @@ def deriv_back(
|
|||||||
|
|
||||||
dx_h_expanded = numpy.meshgrid(*dx_h, indexing='ij')
|
dx_h_expanded = numpy.meshgrid(*dx_h, indexing='ij')
|
||||||
|
|
||||||
def deriv(axis: int) -> sparse.spmatrix:
|
def deriv(axis: int) -> sparse.sparray:
|
||||||
return shift_circ(axis, shape, -1) - sparse.eye(n)
|
return shift_circ(axis, shape, -1) - sparse.eye_array(n)
|
||||||
|
|
||||||
Ds = [sparse.diags(-1 / dx.ravel(order='C')) @ deriv(a)
|
Ds = [sparse.diags_array(-1 / dx.ravel(order='C')) @ deriv(a)
|
||||||
for a, dx in enumerate(dx_h_expanded)]
|
for a, dx in enumerate(dx_h_expanded)]
|
||||||
|
|
||||||
return Ds
|
return Ds
|
||||||
|
|
||||||
|
|
||||||
def cross(
|
def cross(
|
||||||
B: Sequence[sparse.spmatrix],
|
B: Sequence[sparse.sparray],
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Cross product operator
|
Cross product operator
|
||||||
|
|
||||||
@ -163,13 +164,14 @@ def cross(
|
|||||||
Sparse matrix corresponding to (B x), where x is the cross product.
|
Sparse matrix corresponding to (B x), where x is the cross product.
|
||||||
"""
|
"""
|
||||||
n = B[0].shape[0]
|
n = B[0].shape[0]
|
||||||
zero = sparse.csr_matrix((n, n))
|
zero = sparse.csr_array((n, n))
|
||||||
return sparse.bmat([[zero, -B[2], B[1]],
|
return sparse.block_array([
|
||||||
|
[zero, -B[2], B[1]],
|
||||||
[B[2], zero, -B[0]],
|
[B[2], zero, -B[0]],
|
||||||
[-B[1], B[0], zero]])
|
[-B[1], B[0], zero]])
|
||||||
|
|
||||||
|
|
||||||
def vec_cross(b: vfdfield_t) -> sparse.spmatrix:
|
def vec_cross(b: vfdfield_t) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Vector cross product operator
|
Vector cross product operator
|
||||||
|
|
||||||
@ -181,11 +183,11 @@ def vec_cross(b: vfdfield_t) -> sparse.spmatrix:
|
|||||||
Sparse matrix corresponding to (b x), where x is the cross product.
|
Sparse matrix corresponding to (b x), where x is the cross product.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
B = [sparse.diags(c) for c in numpy.split(b, 3)]
|
B = [sparse.diags_array(c) for c in numpy.split(b, 3)]
|
||||||
return cross(B)
|
return cross(B)
|
||||||
|
|
||||||
|
|
||||||
def avg_forward(axis: int, shape: Sequence[int]) -> sparse.spmatrix:
|
def avg_forward(axis: int, shape: Sequence[int]) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Forward average operator `(x4 = (x4 + x5) / 2)`
|
Forward average operator `(x4 = (x4 + x5) / 2)`
|
||||||
|
|
||||||
@ -200,10 +202,10 @@ def avg_forward(axis: int, shape: Sequence[int]) -> sparse.spmatrix:
|
|||||||
raise Exception(f'Invalid shape: {shape}')
|
raise Exception(f'Invalid shape: {shape}')
|
||||||
|
|
||||||
n = numpy.prod(shape)
|
n = numpy.prod(shape)
|
||||||
return 0.5 * (sparse.eye(n) + shift_circ(axis, shape))
|
return 0.5 * (sparse.eye_array(n) + shift_circ(axis, shape))
|
||||||
|
|
||||||
|
|
||||||
def avg_back(axis: int, shape: Sequence[int]) -> sparse.spmatrix:
|
def avg_back(axis: int, shape: Sequence[int]) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Backward average operator `(x4 = (x4 + x3) / 2)`
|
Backward average operator `(x4 = (x4 + x3) / 2)`
|
||||||
|
|
||||||
@ -218,8 +220,8 @@ def avg_back(axis: int, shape: Sequence[int]) -> sparse.spmatrix:
|
|||||||
|
|
||||||
|
|
||||||
def curl_forward(
|
def curl_forward(
|
||||||
dx_e: Sequence[NDArray[numpy.float_]],
|
dx_e: Sequence[NDArray[floating | complexfloating]],
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Curl operator for use with the E field.
|
Curl operator for use with the E field.
|
||||||
|
|
||||||
@ -234,8 +236,8 @@ def curl_forward(
|
|||||||
|
|
||||||
|
|
||||||
def curl_back(
|
def curl_back(
|
||||||
dx_h: Sequence[NDArray[numpy.float_]],
|
dx_h: Sequence[NDArray[floating | complexfloating]],
|
||||||
) -> sparse.spmatrix:
|
) -> sparse.sparray:
|
||||||
"""
|
"""
|
||||||
Curl operator for use with the H field.
|
Curl operator for use with the H field.
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +1,65 @@
|
|||||||
"""
|
"""
|
||||||
Types shared across multiple submodules
|
Types shared across multiple submodules
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Callable, MutableSequence
|
from typing import NewType
|
||||||
import numpy
|
from collections.abc import Sequence, Callable, MutableSequence
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
from numpy import floating, complexfloating
|
||||||
|
|
||||||
|
|
||||||
# Field types
|
# Field types
|
||||||
fdfield_t = NDArray[numpy.float_]
|
fdfield_t = NewType('fdfield_t', NDArray[floating])
|
||||||
|
type fdfield = fdfield_t | NDArray[floating]
|
||||||
"""Vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)"""
|
"""Vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)"""
|
||||||
|
|
||||||
vfdfield_t = NDArray[numpy.float_]
|
vfdfield_t = NewType('vfdfield_t', NDArray[floating])
|
||||||
|
type vfdfield = vfdfield_t | NDArray[floating]
|
||||||
"""Linearized vector field (single vector of length 3*X*Y*Z)"""
|
"""Linearized vector field (single vector of length 3*X*Y*Z)"""
|
||||||
|
|
||||||
cfdfield_t = NDArray[numpy.complex_]
|
cfdfield_t = NewType('cfdfield_t', NDArray[complexfloating])
|
||||||
|
type cfdfield = cfdfield_t | NDArray[complexfloating]
|
||||||
"""Complex vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)"""
|
"""Complex vector field with shape (3, X, Y, Z) (e.g. `[E_x, E_y, E_z]`)"""
|
||||||
|
|
||||||
vcfdfield_t = NDArray[numpy.complex_]
|
vcfdfield_t = NewType('vcfdfield_t', NDArray[complexfloating])
|
||||||
|
type vcfdfield = vcfdfield_t | NDArray[complexfloating]
|
||||||
"""Linearized complex vector field (single vector of length 3*X*Y*Z)"""
|
"""Linearized complex vector field (single vector of length 3*X*Y*Z)"""
|
||||||
|
|
||||||
|
|
||||||
dx_lists_t = Sequence[Sequence[NDArray[numpy.float_]]]
|
fdslice_t = NewType('fdslice_t', NDArray[floating])
|
||||||
|
type fdslice = fdslice_t | NDArray[floating]
|
||||||
|
"""Vector field slice with shape (3, X, Y) (e.g. `[E_x, E_y, E_z]` at a single Z position)"""
|
||||||
|
|
||||||
|
vfdslice_t = NewType('vfdslice_t', NDArray[floating])
|
||||||
|
type vfdslice = vfdslice_t | NDArray[floating]
|
||||||
|
"""Linearized vector field slice (single vector of length 3*X*Y)"""
|
||||||
|
|
||||||
|
cfdslice_t = NewType('cfdslice_t', NDArray[complexfloating])
|
||||||
|
type cfdslice = cfdslice_t | NDArray[complexfloating]
|
||||||
|
"""Complex vector field slice with shape (3, X, Y) (e.g. `[E_x, E_y, E_z]` at a single Z position)"""
|
||||||
|
|
||||||
|
vcfdslice_t = NewType('vcfdslice_t', NDArray[complexfloating])
|
||||||
|
type vcfdslice = vcfdslice_t | NDArray[complexfloating]
|
||||||
|
"""Linearized complex vector field slice (single vector of length 3*X*Y)"""
|
||||||
|
|
||||||
|
|
||||||
|
fdfield2_t = NewType('fdfield2_t', NDArray[floating])
|
||||||
|
type fdfield2 = fdfield2_t | NDArray[floating]
|
||||||
|
"""2D Vector field with shape (2, X, Y) (e.g. `[E_x, E_y]`)"""
|
||||||
|
|
||||||
|
vfdfield2_t = NewType('vfdfield2_t', NDArray[floating])
|
||||||
|
type vfdfield2 = vfdfield2_t | NDArray[floating]
|
||||||
|
"""2D Linearized vector field (single vector of length 2*X*Y)"""
|
||||||
|
|
||||||
|
cfdfield2_t = NewType('cfdfield2_t', NDArray[complexfloating])
|
||||||
|
type cfdfield2 = cfdfield2_t | NDArray[complexfloating]
|
||||||
|
"""2D Complex vector field with shape (2, X, Y) (e.g. `[E_x, E_y]`)"""
|
||||||
|
|
||||||
|
vcfdfield2_t = NewType('vcfdfield2_t', NDArray[complexfloating])
|
||||||
|
type vcfdfield2 = vcfdfield2_t | NDArray[complexfloating]
|
||||||
|
"""2D Linearized complex vector field (single vector of length 2*X*Y)"""
|
||||||
|
|
||||||
|
|
||||||
|
dx_lists_t = Sequence[Sequence[NDArray[floating | complexfloating]]]
|
||||||
"""
|
"""
|
||||||
'dxes' datastructure which contains grid cell width information in the following format:
|
'dxes' datastructure which contains grid cell width information in the following format:
|
||||||
|
|
||||||
@ -31,9 +70,23 @@ dx_lists_t = Sequence[Sequence[NDArray[numpy.float_]]]
|
|||||||
and `dy_h[0]` is the y-width of the `y=0` cells, as used when calculating dH/dy, etc.
|
and `dy_h[0]` is the y-width of the `y=0` cells, as used when calculating dH/dy, etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
dx_lists_mut = MutableSequence[MutableSequence[NDArray[numpy.float_]]]
|
dx_lists2_t = Sequence[Sequence[NDArray[floating | complexfloating]]]
|
||||||
|
"""
|
||||||
|
2D 'dxes' datastructure which contains grid cell width information in the following format:
|
||||||
|
|
||||||
|
[[[dx_e[0], dx_e[1], ...], [dy_e[0], ...]],
|
||||||
|
[[dx_h[0], dx_h[1], ...], [dy_h[0], ...]]]
|
||||||
|
|
||||||
|
where `dx_e[0]` is the x-width of the `x=0` cells, as used when calculating dE/dx,
|
||||||
|
and `dy_h[0]` is the y-width of the `y=0` cells, as used when calculating dH/dy, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
dx_lists_mut = MutableSequence[MutableSequence[NDArray[floating | complexfloating]]]
|
||||||
"""Mutable version of `dx_lists_t`"""
|
"""Mutable version of `dx_lists_t`"""
|
||||||
|
|
||||||
|
dx_lists2_mut = MutableSequence[MutableSequence[NDArray[floating | complexfloating]]]
|
||||||
|
"""Mutable version of `dx_lists2_t`"""
|
||||||
|
|
||||||
|
|
||||||
fdfield_updater_t = Callable[..., fdfield_t]
|
fdfield_updater_t = Callable[..., fdfield_t]
|
||||||
"""Convenience type for functions which take and return an fdfield_t"""
|
"""Convenience type for functions which take and return an fdfield_t"""
|
||||||
|
|||||||
@ -4,11 +4,16 @@ and a 1D array representation of that field `[f_x0, f_x1, f_x2,... f_y0,... f_z0
|
|||||||
Vectorized versions of the field use row-major (ie., C-style) ordering.
|
Vectorized versions of the field use row-major (ie., C-style) ordering.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import overload, Sequence
|
from typing import overload
|
||||||
|
from collections.abc import Sequence
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike, NDArray
|
||||||
|
|
||||||
from .types import fdfield_t, vfdfield_t, cfdfield_t, vcfdfield_t
|
from .types import (
|
||||||
|
fdfield_t, vfdfield_t, cfdfield_t, vcfdfield_t,
|
||||||
|
fdslice_t, vfdslice_t, cfdslice_t, vcfdslice_t,
|
||||||
|
fdfield2_t, vfdfield2_t, cfdfield2_t, vcfdfield2_t,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
@ -24,17 +29,35 @@ def vec(f: cfdfield_t) -> vcfdfield_t:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def vec(f: ArrayLike) -> vfdfield_t | vcfdfield_t:
|
def vec(f: fdfield2_t) -> vfdfield2_t:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def vec(f: fdfield_t | cfdfield_t | ArrayLike | None) -> vfdfield_t | vcfdfield_t | None:
|
@overload
|
||||||
|
def vec(f: cfdfield2_t) -> vcfdfield2_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def vec(f: fdslice_t) -> vfdslice_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def vec(f: cfdslice_t) -> vcfdslice_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def vec(f: ArrayLike) -> NDArray:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def vec(
|
||||||
|
f: fdfield_t | cfdfield_t | fdfield2_t | cfdfield2_t | fdslice_t | cfdslice_t | ArrayLike | None,
|
||||||
|
) -> vfdfield_t | vcfdfield_t | vfdfield2_t | vcfdfield2_t | vfdslice_t | vcfdslice_t | NDArray | None:
|
||||||
"""
|
"""
|
||||||
Create a 1D ndarray from a 3D vector field which spans a 1-3D region.
|
Create a 1D ndarray from a vector field which spans a 1-3D region.
|
||||||
|
|
||||||
Returns `None` if called with `f=None`.
|
Returns `None` if called with `f=None`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
f: A vector field, `[f_x, f_y, f_z]` where each `f_` component is a 1- to
|
f: A vector field, e.g. `[f_x, f_y, f_z]` where each `f_` component is a 1- to
|
||||||
3-D ndarray (`f_*` should all be the same size). Doesn't fail with `f=None`.
|
3-D ndarray (`f_*` should all be the same size). Doesn't fail with `f=None`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -42,37 +65,62 @@ def vec(f: fdfield_t | cfdfield_t | ArrayLike | None) -> vfdfield_t | vcfdfield_
|
|||||||
"""
|
"""
|
||||||
if f is None:
|
if f is None:
|
||||||
return None
|
return None
|
||||||
return numpy.ravel(f, order='C')
|
return numpy.ravel(f, order='C') # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def unvec(v: None, shape: Sequence[int]) -> None:
|
def unvec(v: None, shape: Sequence[int], nvdim: int = 3) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def unvec(v: vfdfield_t, shape: Sequence[int]) -> fdfield_t:
|
def unvec(v: vfdfield_t, shape: Sequence[int], nvdim: int = 3) -> fdfield_t:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def unvec(v: vcfdfield_t, shape: Sequence[int]) -> cfdfield_t:
|
def unvec(v: vcfdfield_t, shape: Sequence[int], nvdim: int = 3) -> cfdfield_t:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unvec(v: vfdfield_t | vcfdfield_t | None, shape: Sequence[int]) -> fdfield_t | cfdfield_t | None:
|
@overload
|
||||||
|
def unvec(v: vfdfield2_t, shape: Sequence[int], nvdim: int = 3) -> fdfield2_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def unvec(v: vcfdfield2_t, shape: Sequence[int], nvdim: int = 3) -> cfdfield2_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def unvec(v: vfdslice_t, shape: Sequence[int], nvdim: int = 3) -> fdslice_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def unvec(v: vcfdslice_t, shape: Sequence[int], nvdim: int = 3) -> cfdslice_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def unvec(v: ArrayLike, shape: Sequence[int], nvdim: int = 3) -> NDArray:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unvec(
|
||||||
|
v: vfdfield_t | vcfdfield_t | vfdfield2_t | vcfdfield2_t | vfdslice_t | vcfdslice_t | ArrayLike | None,
|
||||||
|
shape: Sequence[int],
|
||||||
|
nvdim: int = 3,
|
||||||
|
) -> fdfield_t | cfdfield_t | fdfield2_t | cfdfield2_t | fdslice_t | cfdslice_t | NDArray | None:
|
||||||
"""
|
"""
|
||||||
Perform the inverse of vec(): take a 1D ndarray and output a 3D field
|
Perform the inverse of vec(): take a 1D ndarray and output an `nvdim`-component field
|
||||||
of form `[f_x, f_y, f_z]` where each of `f_*` is a len(shape)-dimensional
|
of form e.g. `[f_x, f_y, f_z]` (`nvdim=3`) where each of `f_*` is a len(shape)-dimensional
|
||||||
ndarray.
|
ndarray.
|
||||||
|
|
||||||
Returns `None` if called with `v=None`.
|
Returns `None` if called with `v=None`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
v: 1D ndarray representing a 3D vector field of shape shape (or None)
|
v: 1D ndarray representing a vector field of shape shape (or None)
|
||||||
shape: shape of the vector field
|
shape: shape of the vector field
|
||||||
|
nvdim: Number of components in each vector
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
`[f_x, f_y, f_z]` where each `f_` is a `len(shape)` dimensional ndarray (or `None`)
|
`[f_x, f_y, f_z]` where each `f_` is a `len(shape)` dimensional ndarray (or `None`)
|
||||||
"""
|
"""
|
||||||
if v is None:
|
if v is None:
|
||||||
return None
|
return None
|
||||||
return v.reshape((3, *shape), order='C')
|
return v.reshape((nvdim, *shape), order='C') # type: ignore
|
||||||
|
|
||||||
|
|||||||
@ -159,8 +159,22 @@ Boundary conditions
|
|||||||
# TODO notes about boundaries / PMLs
|
# TODO notes about boundaries / PMLs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .base import maxwell_e, maxwell_h
|
from .base import (
|
||||||
from .pml import cpml_params, updates_with_cpml
|
maxwell_e as maxwell_e,
|
||||||
from .energy import (poynting, poynting_divergence, energy_hstep, energy_estep,
|
maxwell_h as maxwell_h,
|
||||||
delta_energy_h2e, delta_energy_j)
|
)
|
||||||
from .boundaries import conducting_boundary
|
from .pml import (
|
||||||
|
cpml_params as cpml_params,
|
||||||
|
updates_with_cpml as updates_with_cpml,
|
||||||
|
)
|
||||||
|
from .energy import (
|
||||||
|
poynting as poynting,
|
||||||
|
poynting_divergence as poynting_divergence,
|
||||||
|
energy_hstep as energy_hstep,
|
||||||
|
energy_estep as energy_estep,
|
||||||
|
delta_energy_h2e as delta_energy_h2e,
|
||||||
|
delta_energy_j as delta_energy_j,
|
||||||
|
)
|
||||||
|
from .boundaries import (
|
||||||
|
conducting_boundary as conducting_boundary,
|
||||||
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
from ..fdmath import dx_lists_t, fdfield_t
|
from ..fdmath import dx_lists_t, fdfield_t, fdfield
|
||||||
from ..fdmath.functional import deriv_back
|
from ..fdmath.functional import deriv_back
|
||||||
|
|
||||||
|
|
||||||
@ -8,8 +8,8 @@ from ..fdmath.functional import deriv_back
|
|||||||
|
|
||||||
|
|
||||||
def poynting(
|
def poynting(
|
||||||
e: fdfield_t,
|
e: fdfield,
|
||||||
h: fdfield_t,
|
h: fdfield,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
r"""
|
r"""
|
||||||
@ -84,14 +84,14 @@ def poynting(
|
|||||||
s[0] = numpy.roll(ey, -1, axis=0) * hz - numpy.roll(ez, -1, axis=0) * hy
|
s[0] = numpy.roll(ey, -1, axis=0) * hz - numpy.roll(ez, -1, axis=0) * hy
|
||||||
s[1] = numpy.roll(ez, -1, axis=1) * hx - numpy.roll(ex, -1, axis=1) * hz
|
s[1] = numpy.roll(ez, -1, axis=1) * hx - numpy.roll(ex, -1, axis=1) * hz
|
||||||
s[2] = numpy.roll(ex, -1, axis=2) * hy - numpy.roll(ey, -1, axis=2) * hx
|
s[2] = numpy.roll(ex, -1, axis=2) * hy - numpy.roll(ey, -1, axis=2) * hx
|
||||||
return s
|
return fdfield_t(s)
|
||||||
|
|
||||||
|
|
||||||
def poynting_divergence(
|
def poynting_divergence(
|
||||||
s: fdfield_t | None = None,
|
s: fdfield | None = None,
|
||||||
*,
|
*,
|
||||||
e: fdfield_t | None = None,
|
e: fdfield | None = None,
|
||||||
h: fdfield_t | None = None,
|
h: fdfield | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -122,15 +122,15 @@ def poynting_divergence(
|
|||||||
|
|
||||||
Dx, Dy, Dz = deriv_back()
|
Dx, Dy, Dz = deriv_back()
|
||||||
ds = Dx(s[0]) + Dy(s[1]) + Dz(s[2])
|
ds = Dx(s[0]) + Dy(s[1]) + Dz(s[2])
|
||||||
return ds
|
return fdfield_t(ds)
|
||||||
|
|
||||||
|
|
||||||
def energy_hstep(
|
def energy_hstep(
|
||||||
e0: fdfield_t,
|
e0: fdfield,
|
||||||
h1: fdfield_t,
|
h1: fdfield,
|
||||||
e2: fdfield_t,
|
e2: fdfield,
|
||||||
epsilon: fdfield_t | None = None,
|
epsilon: fdfield | None = None,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -150,15 +150,15 @@ def energy_hstep(
|
|||||||
Energy, at the time of the H-field `h1`.
|
Energy, at the time of the H-field `h1`.
|
||||||
"""
|
"""
|
||||||
u = dxmul(e0 * e2, h1 * h1, epsilon, mu, dxes)
|
u = dxmul(e0 * e2, h1 * h1, epsilon, mu, dxes)
|
||||||
return u
|
return fdfield_t(u)
|
||||||
|
|
||||||
|
|
||||||
def energy_estep(
|
def energy_estep(
|
||||||
h0: fdfield_t,
|
h0: fdfield,
|
||||||
e1: fdfield_t,
|
e1: fdfield,
|
||||||
h2: fdfield_t,
|
h2: fdfield,
|
||||||
epsilon: fdfield_t | None = None,
|
epsilon: fdfield | None = None,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -178,17 +178,17 @@ def energy_estep(
|
|||||||
Energy, at the time of the E-field `e1`.
|
Energy, at the time of the E-field `e1`.
|
||||||
"""
|
"""
|
||||||
u = dxmul(e1 * e1, h0 * h2, epsilon, mu, dxes)
|
u = dxmul(e1 * e1, h0 * h2, epsilon, mu, dxes)
|
||||||
return u
|
return fdfield_t(u)
|
||||||
|
|
||||||
|
|
||||||
def delta_energy_h2e(
|
def delta_energy_h2e(
|
||||||
dt: float,
|
dt: float,
|
||||||
e0: fdfield_t,
|
e0: fdfield,
|
||||||
h1: fdfield_t,
|
h1: fdfield,
|
||||||
e2: fdfield_t,
|
e2: fdfield,
|
||||||
h3: fdfield_t,
|
h3: fdfield,
|
||||||
epsilon: fdfield_t | None = None,
|
epsilon: fdfield | None = None,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -211,17 +211,17 @@ def delta_energy_h2e(
|
|||||||
de = e2 * (e2 - e0) / dt
|
de = e2 * (e2 - e0) / dt
|
||||||
dh = h1 * (h3 - h1) / dt
|
dh = h1 * (h3 - h1) / dt
|
||||||
du = dxmul(de, dh, epsilon, mu, dxes)
|
du = dxmul(de, dh, epsilon, mu, dxes)
|
||||||
return du
|
return fdfield_t(du)
|
||||||
|
|
||||||
|
|
||||||
def delta_energy_e2h(
|
def delta_energy_e2h(
|
||||||
dt: float,
|
dt: float,
|
||||||
h0: fdfield_t,
|
h0: fdfield,
|
||||||
e1: fdfield_t,
|
e1: fdfield,
|
||||||
h2: fdfield_t,
|
h2: fdfield,
|
||||||
e3: fdfield_t,
|
e3: fdfield,
|
||||||
epsilon: fdfield_t | None = None,
|
epsilon: fdfield | None = None,
|
||||||
mu: fdfield_t | None = None,
|
mu: fdfield | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -244,12 +244,12 @@ def delta_energy_e2h(
|
|||||||
de = e1 * (e3 - e1) / dt
|
de = e1 * (e3 - e1) / dt
|
||||||
dh = h2 * (h2 - h0) / dt
|
dh = h2 * (h2 - h0) / dt
|
||||||
du = dxmul(de, dh, epsilon, mu, dxes)
|
du = dxmul(de, dh, epsilon, mu, dxes)
|
||||||
return du
|
return fdfield_t(du)
|
||||||
|
|
||||||
|
|
||||||
def delta_energy_j(
|
def delta_energy_j(
|
||||||
j0: fdfield_t,
|
j0: fdfield,
|
||||||
e1: fdfield_t,
|
e1: fdfield,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
"""
|
"""
|
||||||
@ -267,14 +267,14 @@ def delta_energy_j(
|
|||||||
* dxes[0][0][:, None, None]
|
* dxes[0][0][:, None, None]
|
||||||
* dxes[0][1][None, :, None]
|
* dxes[0][1][None, :, None]
|
||||||
* dxes[0][2][None, None, :])
|
* dxes[0][2][None, None, :])
|
||||||
return du
|
return fdfield_t(du)
|
||||||
|
|
||||||
|
|
||||||
def dxmul(
|
def dxmul(
|
||||||
ee: fdfield_t,
|
ee: fdfield,
|
||||||
hh: fdfield_t,
|
hh: fdfield,
|
||||||
epsilon: fdfield_t | float | None = None,
|
epsilon: fdfield | float | None = None,
|
||||||
mu: fdfield_t | float | None = None,
|
mu: fdfield | float | None = None,
|
||||||
dxes: dx_lists_t | None = None,
|
dxes: dx_lists_t | None = None,
|
||||||
) -> fdfield_t:
|
) -> fdfield_t:
|
||||||
if epsilon is None:
|
if epsilon is None:
|
||||||
@ -292,4 +292,4 @@ def dxmul(
|
|||||||
* dxes[1][0][:, None, None]
|
* dxes[1][0][:, None, None]
|
||||||
* dxes[1][1][None, :, None]
|
* dxes[1][1][None, :, None]
|
||||||
* dxes[1][2][None, None, :])
|
* dxes[1][2][None, None, :])
|
||||||
return result
|
return fdfield_t(result)
|
||||||
|
|||||||
166
meanas/fdtd/misc.py
Normal file
166
meanas/fdtd/misc.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
from numpy.typing import NDArray, ArrayLike
|
||||||
|
from numpy import pi
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
pulse_fn_t = Callable[[int | NDArray], tuple[float, float, float]]
|
||||||
|
|
||||||
|
|
||||||
|
def gaussian_packet(
|
||||||
|
wl: float,
|
||||||
|
dwl: float,
|
||||||
|
dt: float,
|
||||||
|
turn_on: float = 1e-10,
|
||||||
|
one_sided: bool = False,
|
||||||
|
) -> tuple[pulse_fn_t, float]:
|
||||||
|
"""
|
||||||
|
Gaussian pulse (or gaussian ramp) for FDTD excitation
|
||||||
|
|
||||||
|
exp(-a*t*t) ==> exp(-omega * omega / (4 * a)) [fourier, ignoring leading const.]
|
||||||
|
|
||||||
|
FWHM_time is 2 * sqrt(2 * log(2)) * sqrt(2 / a)
|
||||||
|
FWHM_omega is 2 * sqrt(2 * log(2)) * sqrt(2 * a) = 4 * sqrt(log(2) * a)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wl: wavelength
|
||||||
|
dwl: Gaussian's FWHM in wavelength space
|
||||||
|
dt: Timestep
|
||||||
|
turn_on: Max allowable amplitude at t=0
|
||||||
|
one_sided: If `True`, source amplitude never decreases after reaching max
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Source function: src(timestep) -> (envelope[tt], cos[... * tt], sin[... * tt])
|
||||||
|
Delay: number of initial timesteps for which envelope[tt] will be 0
|
||||||
|
"""
|
||||||
|
logger.warning('meanas.fdtd.misc functions are still very WIP!') # TODO
|
||||||
|
# dt * dw = 4 * ln(2)
|
||||||
|
|
||||||
|
omega = 2 * pi / wl
|
||||||
|
freq = 1 / wl
|
||||||
|
fwhm_omega = dwl * omega * omega / (2 * pi) # dwl -> d_omega (approx)
|
||||||
|
alpha = (fwhm_omega * fwhm_omega) * numpy.log(2) / 8
|
||||||
|
delay = numpy.sqrt(-numpy.log(turn_on) / alpha)
|
||||||
|
delay = numpy.ceil(delay * freq) / freq # force delay to integer number of periods to maintain phase
|
||||||
|
logger.info(f'src_time {2 * delay / dt}')
|
||||||
|
|
||||||
|
def source_phasor(ii: int | NDArray) -> tuple[float, float, float]:
|
||||||
|
t0 = ii * dt - delay
|
||||||
|
envelope = numpy.sqrt(numpy.sqrt(2 * alpha / pi)) * numpy.exp(-alpha * t0 * t0)
|
||||||
|
|
||||||
|
if one_sided and t0 > 0:
|
||||||
|
envelope = 1
|
||||||
|
|
||||||
|
cc = numpy.cos(omega * t0)
|
||||||
|
ss = numpy.sin(omega * t0)
|
||||||
|
|
||||||
|
return envelope, cc, ss
|
||||||
|
|
||||||
|
# nrm = numpy.exp(-omega * omega / alpha) / 2
|
||||||
|
|
||||||
|
return source_phasor, delay
|
||||||
|
|
||||||
|
|
||||||
|
def ricker_pulse(
|
||||||
|
wl: float,
|
||||||
|
dt: float,
|
||||||
|
turn_on: float = 1e-10,
|
||||||
|
) -> tuple[pulse_fn_t, float]:
|
||||||
|
"""
|
||||||
|
Ricker wavelet (second derivative of a gaussian pulse)
|
||||||
|
|
||||||
|
t0 = ii * dt - delay
|
||||||
|
R = w_peak * t0 / 2
|
||||||
|
f(t) = (1 - 2 * (pi * f_peak * t0) ** 2) * exp(-(pi * f_peak * t0)**2
|
||||||
|
= (1 - (w_peak * t0)**2 / 2 exp(-(w_peak * t0 / 2) **2)
|
||||||
|
= (1 - 2 * R * R) * exp(-R * R)
|
||||||
|
|
||||||
|
# NOTE: don't use cosine/sine for J, just for phasor readout
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wl: wavelength
|
||||||
|
dt: Timestep
|
||||||
|
turn_on: Max allowable amplitude at t=0
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Source function: src(timestep) -> (envelope[tt], cos[... * tt], sin[... * tt])
|
||||||
|
Delay: number of initial timesteps for which envelope[tt] will be 0
|
||||||
|
"""
|
||||||
|
logger.warning('meanas.fdtd.misc functions are still very WIP!') # TODO
|
||||||
|
omega = 2 * pi / wl
|
||||||
|
freq = 1 / wl
|
||||||
|
# r0 = omega / 2
|
||||||
|
|
||||||
|
from scipy.optimize import root_scalar
|
||||||
|
delay_results = root_scalar(lambda tt: (1 - omega * omega * tt * tt / 2) * numpy.exp(-omega * omega / 4 * tt * tt) - turn_on, x0=0, x1=-2 / omega)
|
||||||
|
delay = delay_results.root
|
||||||
|
delay = numpy.ceil(delay * freq) / freq # force delay to integer number of periods to maintain phase
|
||||||
|
|
||||||
|
def source_phasor(ii: int | NDArray) -> tuple[float, float, float]:
|
||||||
|
t0 = ii * dt - delay
|
||||||
|
rr = omega * t0 / 2
|
||||||
|
ff = (1 - 2 * rr * rr) * numpy.exp(-rr * rr)
|
||||||
|
|
||||||
|
cc = numpy.cos(omega * t0)
|
||||||
|
ss = numpy.sin(omega * t0)
|
||||||
|
|
||||||
|
return ff, cc, ss
|
||||||
|
|
||||||
|
return source_phasor, delay
|
||||||
|
|
||||||
|
|
||||||
|
def gaussian_beam(
|
||||||
|
xyz: list[NDArray],
|
||||||
|
center: ArrayLike,
|
||||||
|
waist_radius: float,
|
||||||
|
wl: float,
|
||||||
|
tilt: float = 0,
|
||||||
|
) -> NDArray[numpy.complex128]:
|
||||||
|
"""
|
||||||
|
Gaussian beam
|
||||||
|
(solution to paraxial Helmholtz equation)
|
||||||
|
|
||||||
|
Default (no tilt) corresponds to a beam propagating in the -z direction.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
xyz: List of [[x0, x1, ...], [y0, ...], [z0, ...]] positions specifying grid
|
||||||
|
locations at which the field will be sampled.
|
||||||
|
center: [x, y, z] location of beam waist
|
||||||
|
waist_radius: Beam radius at the waist
|
||||||
|
wl: Wavelength
|
||||||
|
tilt: Rotation around y axis. Default (0) has beam propagating in -z direction.
|
||||||
|
"""
|
||||||
|
logger.warning('meanas.fdtd.misc functions are still very WIP!') # TODO
|
||||||
|
w0 = waist_radius
|
||||||
|
grids = numpy.asarray(numpy.meshgrid(*xyz, indexing='ij'))
|
||||||
|
grids -= numpy.asarray(center)[:, None, None, None]
|
||||||
|
|
||||||
|
rot = numpy.array([
|
||||||
|
[ numpy.cos(tilt), 0, numpy.sin(tilt)],
|
||||||
|
[ 0, 1, 0],
|
||||||
|
[-numpy.sin(tilt), 0, numpy.cos(tilt)],
|
||||||
|
])
|
||||||
|
|
||||||
|
xx, yy, zz = numpy.einsum('ij,jxyz->ixyz', rot, grids)
|
||||||
|
r2 = xx * xx + yy * yy
|
||||||
|
z2 = zz * zz
|
||||||
|
|
||||||
|
zr = pi * w0 * w0 / wl
|
||||||
|
zr2 = zr * zr
|
||||||
|
wz2 = w0 * w0 * (1 + z2 / zr2)
|
||||||
|
wz = numpy.sqrt(wz2) # == fwhm(z) / sqrt(2 * ln(2))
|
||||||
|
|
||||||
|
kk = 2 * pi / wl
|
||||||
|
Rz = zz * (1 + zr2 / z2)
|
||||||
|
gouy = numpy.arctan(zz / zr)
|
||||||
|
|
||||||
|
gaussian = w0 / wz * numpy.exp(-r2 / wz2) * numpy.exp(1j * (kk * zz + kk * r2 / 2 / Rz - gouy))
|
||||||
|
|
||||||
|
row = gaussian[:, :, gaussian.shape[2] // 2]
|
||||||
|
norm = numpy.sqrt((row * row.conj()).sum())
|
||||||
|
return gaussian / norm
|
||||||
@ -7,12 +7,13 @@ PML implementations
|
|||||||
"""
|
"""
|
||||||
# TODO retest pmls!
|
# TODO retest pmls!
|
||||||
|
|
||||||
from typing import Callable, Sequence, Any
|
from typing import Any
|
||||||
|
from collections.abc import Callable, Sequence
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray, DTypeLike
|
from numpy.typing import NDArray, DTypeLike
|
||||||
|
|
||||||
from ..fdmath import fdfield_t, dx_lists_t
|
from ..fdmath import fdfield, fdfield_t, dx_lists_t
|
||||||
from ..fdmath.functional import deriv_forward, deriv_back
|
from ..fdmath.functional import deriv_forward, deriv_back
|
||||||
|
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ def updates_with_cpml(
|
|||||||
cpml_params: Sequence[Sequence[dict[str, Any] | None]],
|
cpml_params: Sequence[Sequence[dict[str, Any] | None]],
|
||||||
dt: float,
|
dt: float,
|
||||||
dxes: dx_lists_t,
|
dxes: dx_lists_t,
|
||||||
epsilon: fdfield_t,
|
epsilon: fdfield,
|
||||||
*,
|
*,
|
||||||
dtype: DTypeLike = numpy.float32,
|
dtype: DTypeLike = numpy.float32,
|
||||||
) -> tuple[Callable[[fdfield_t, fdfield_t, fdfield_t], None],
|
) -> tuple[Callable[[fdfield_t, fdfield_t, fdfield_t], None],
|
||||||
@ -111,7 +112,7 @@ def updates_with_cpml(
|
|||||||
params_H: list[list[tuple[Any, Any, Any, Any]]] = deepcopy(params_E)
|
params_H: list[list[tuple[Any, Any, Any, Any]]] = deepcopy(params_E)
|
||||||
|
|
||||||
for axis in range(3):
|
for axis in range(3):
|
||||||
for pp, polarity in enumerate((-1, 1)):
|
for pp, _polarity in enumerate((-1, 1)):
|
||||||
cpml_param = cpml_params[axis][pp]
|
cpml_param = cpml_params[axis][pp]
|
||||||
if cpml_param is None:
|
if cpml_param is None:
|
||||||
psi_E[axis][pp] = (None, None)
|
psi_E[axis][pp] = (None, None)
|
||||||
@ -184,7 +185,7 @@ def updates_with_cpml(
|
|||||||
def update_H(
|
def update_H(
|
||||||
e: fdfield_t,
|
e: fdfield_t,
|
||||||
h: fdfield_t,
|
h: fdfield_t,
|
||||||
mu: fdfield_t = numpy.ones(3),
|
mu: fdfield_t | tuple[int, int, int] = (1, 1, 1),
|
||||||
) -> None:
|
) -> None:
|
||||||
dyEx = Dfy(e[0])
|
dyEx = Dfy(e[0])
|
||||||
dzEx = Dfz(e[0])
|
dzEx = Dfz(e[0])
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
Test fixtures
|
Test fixtures
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Iterable, Any
|
# ruff: noqa: ARG001
|
||||||
|
from typing import Any
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
import pytest # type: ignore
|
import pytest # type: ignore
|
||||||
@ -20,18 +21,18 @@ FixtureRequest = Any
|
|||||||
(5, 5, 5),
|
(5, 5, 5),
|
||||||
# (7, 7, 7),
|
# (7, 7, 7),
|
||||||
])
|
])
|
||||||
def shape(request: FixtureRequest) -> Iterable[tuple[int, ...]]:
|
def shape(request: FixtureRequest) -> tuple[int, ...]:
|
||||||
yield (3, *request.param)
|
return (3, *request.param)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=[1.0, 1.5])
|
@pytest.fixture(scope='module', params=[1.0, 1.5])
|
||||||
def epsilon_bg(request: FixtureRequest) -> Iterable[float]:
|
def epsilon_bg(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=[1.0, 2.5])
|
@pytest.fixture(scope='module', params=[1.0, 2.5])
|
||||||
def epsilon_fg(request: FixtureRequest) -> Iterable[float]:
|
def epsilon_fg(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=['center', '000', 'random'])
|
@pytest.fixture(scope='module', params=['center', '000', 'random'])
|
||||||
@ -40,7 +41,7 @@ def epsilon(
|
|||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
epsilon_bg: float,
|
epsilon_bg: float,
|
||||||
epsilon_fg: float,
|
epsilon_fg: float,
|
||||||
) -> Iterable[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64]:
|
||||||
is3d = (numpy.array(shape) == 1).sum() == 0
|
is3d = (numpy.array(shape) == 1).sum() == 0
|
||||||
if is3d:
|
if is3d:
|
||||||
if request.param == '000':
|
if request.param == '000':
|
||||||
@ -60,17 +61,17 @@ def epsilon(
|
|||||||
high=max(epsilon_bg, epsilon_fg),
|
high=max(epsilon_bg, epsilon_fg),
|
||||||
size=shape)
|
size=shape)
|
||||||
|
|
||||||
yield epsilon
|
return epsilon
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=[1.0]) # 1.5
|
@pytest.fixture(scope='module', params=[1.0]) # 1.5
|
||||||
def j_mag(request: FixtureRequest) -> Iterable[float]:
|
def j_mag(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=[1.0, 1.5])
|
@pytest.fixture(scope='module', params=[1.0, 1.5])
|
||||||
def dx(request: FixtureRequest) -> Iterable[float]:
|
def dx(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='module', params=['uniform', 'centerbig'])
|
@pytest.fixture(scope='module', params=['uniform', 'centerbig'])
|
||||||
@ -78,7 +79,7 @@ def dxes(
|
|||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
dx: float,
|
dx: float,
|
||||||
) -> Iterable[list[list[NDArray[numpy.float64]]]]:
|
) -> list[list[NDArray[numpy.float64]]]:
|
||||||
if request.param == 'uniform':
|
if request.param == 'uniform':
|
||||||
dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)]
|
dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)]
|
||||||
elif request.param == 'centerbig':
|
elif request.param == 'centerbig':
|
||||||
@ -90,5 +91,5 @@ def dxes(
|
|||||||
dxe = [PRNG.uniform(low=1.0 * dx, high=1.1 * dx, size=s) for s in shape[1:]]
|
dxe = [PRNG.uniform(low=1.0 * dx, high=1.1 * dx, size=s) for s in shape[1:]]
|
||||||
dxh = [(d + numpy.roll(d, -1)) / 2 for d in dxe]
|
dxh = [(d + numpy.roll(d, -1)) / 2 for d in dxe]
|
||||||
dxes = [dxe, dxh]
|
dxes = [dxe, dxh]
|
||||||
yield dxes
|
return dxes
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from typing import Iterable
|
# ruff: noqa: ARG001
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import pytest # type: ignore
|
import pytest # type: ignore
|
||||||
import numpy
|
import numpy
|
||||||
@ -6,7 +6,7 @@ from numpy.typing import NDArray
|
|||||||
#from numpy.testing import assert_allclose, assert_array_equal
|
#from numpy.testing import assert_allclose, assert_array_equal
|
||||||
|
|
||||||
from .. import fdfd
|
from .. import fdfd
|
||||||
from ..fdmath import vec, unvec
|
from ..fdmath import vec, unvec, vcfdfield, vfdfield, dx_lists_t
|
||||||
from .utils import assert_close # , assert_fields_close
|
from .utils import assert_close # , assert_fields_close
|
||||||
from .conftest import FixtureRequest
|
from .conftest import FixtureRequest
|
||||||
|
|
||||||
@ -61,24 +61,24 @@ def test_poynting_planes(sim: 'FDResult') -> None:
|
|||||||
# Also see conftest.py
|
# Also see conftest.py
|
||||||
|
|
||||||
@pytest.fixture(params=[1 / 1500])
|
@pytest.fixture(params=[1 / 1500])
|
||||||
def omega(request: FixtureRequest) -> Iterable[float]:
|
def omega(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[None])
|
@pytest.fixture(params=[None])
|
||||||
def pec(request: FixtureRequest) -> Iterable[NDArray[numpy.float64] | None]:
|
def pec(request: FixtureRequest) -> NDArray[numpy.float64] | None:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[None])
|
@pytest.fixture(params=[None])
|
||||||
def pmc(request: FixtureRequest) -> Iterable[NDArray[numpy.float64] | None]:
|
def pmc(request: FixtureRequest) -> NDArray[numpy.float64] | None:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
#@pytest.fixture(scope='module',
|
#@pytest.fixture(scope='module',
|
||||||
# params=[(25, 5, 5)])
|
# params=[(25, 5, 5)])
|
||||||
#def shape(request):
|
#def shape(request: FixtureRequest):
|
||||||
# yield (3, *request.param)
|
# return (3, *request.param)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['diag']) # 'center'
|
@pytest.fixture(params=['diag']) # 'center'
|
||||||
@ -86,7 +86,7 @@ def j_distribution(
|
|||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
j_mag: float,
|
j_mag: float,
|
||||||
) -> Iterable[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64]:
|
||||||
j = numpy.zeros(shape, dtype=complex)
|
j = numpy.zeros(shape, dtype=complex)
|
||||||
center_mask = numpy.zeros(shape, dtype=bool)
|
center_mask = numpy.zeros(shape, dtype=bool)
|
||||||
center_mask[:, shape[1] // 2, shape[2] // 2, shape[3] // 2] = True
|
center_mask[:, shape[1] // 2, shape[2] // 2, shape[3] // 2] = True
|
||||||
@ -96,22 +96,22 @@ def j_distribution(
|
|||||||
elif request.param == 'diag':
|
elif request.param == 'diag':
|
||||||
j[numpy.roll(center_mask, [1, 1, 1], axis=(1, 2, 3))] = (1 + 1j) * j_mag
|
j[numpy.roll(center_mask, [1, 1, 1], axis=(1, 2, 3))] = (1 + 1j) * j_mag
|
||||||
j[numpy.roll(center_mask, [-1, -1, -1], axis=(1, 2, 3))] = (1 - 1j) * j_mag
|
j[numpy.roll(center_mask, [-1, -1, -1], axis=(1, 2, 3))] = (1 - 1j) * j_mag
|
||||||
yield j
|
return j
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass()
|
@dataclasses.dataclass()
|
||||||
class FDResult:
|
class FDResult:
|
||||||
shape: tuple[int, ...]
|
shape: tuple[int, ...]
|
||||||
dxes: list[list[NDArray[numpy.float64]]]
|
dxes: dx_lists_t
|
||||||
epsilon: NDArray[numpy.float64]
|
epsilon: vfdfield
|
||||||
omega: complex
|
omega: complex
|
||||||
j: NDArray[numpy.complex128]
|
j: vcfdfield
|
||||||
e: NDArray[numpy.complex128]
|
e: vcfdfield
|
||||||
pmc: NDArray[numpy.float64] | None
|
pmc: vfdfield | None
|
||||||
pec: NDArray[numpy.float64] | None
|
pec: vfdfield | None
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def sim(
|
def sim(
|
||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
@ -141,11 +141,11 @@ def sim(
|
|||||||
j_vec = vec(j_distribution)
|
j_vec = vec(j_distribution)
|
||||||
eps_vec = vec(epsilon)
|
eps_vec = vec(epsilon)
|
||||||
e_vec = fdfd.solvers.generic(
|
e_vec = fdfd.solvers.generic(
|
||||||
J=j_vec,
|
J = j_vec,
|
||||||
omega=omega,
|
omega = omega,
|
||||||
dxes=dxes,
|
dxes = dxes,
|
||||||
epsilon=eps_vec,
|
epsilon = eps_vec,
|
||||||
matrix_solver_opts={'atol': 1e-15, 'tol': 1e-11},
|
matrix_solver_opts = dict(atol=1e-15, rtol=1e-11),
|
||||||
)
|
)
|
||||||
e = unvec(e_vec, shape[1:])
|
e = unvec(e_vec, shape[1:])
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
from typing import Iterable
|
# ruff: noqa: ARG001
|
||||||
import pytest # type: ignore
|
import pytest # type: ignore
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
from numpy.testing import assert_allclose
|
from numpy.testing import assert_allclose
|
||||||
|
|
||||||
from .. import fdfd
|
from .. import fdfd
|
||||||
from ..fdmath import vec, unvec, dx_lists_mut
|
from ..fdmath import vec, unvec, dx_lists_mut, vfdfield, cfdfield_t
|
||||||
#from .utils import assert_close, assert_fields_close
|
#from .utils import assert_close, assert_fields_close
|
||||||
from .test_fdfd import FDResult
|
from .test_fdfd import FDResult
|
||||||
from .conftest import FixtureRequest
|
from .conftest import FixtureRequest
|
||||||
@ -44,41 +44,41 @@ def test_pml(sim: FDResult, src_polarity: int) -> None:
|
|||||||
# Also see conftest.py
|
# Also see conftest.py
|
||||||
|
|
||||||
@pytest.fixture(params=[1 / 1500])
|
@pytest.fixture(params=[1 / 1500])
|
||||||
def omega(request: FixtureRequest) -> Iterable[float]:
|
def omega(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[None])
|
@pytest.fixture(params=[None])
|
||||||
def pec(request: FixtureRequest) -> Iterable[NDArray[numpy.float64] | None]:
|
def pec(request: FixtureRequest) -> NDArray[numpy.float64] | None:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[None])
|
@pytest.fixture(params=[None])
|
||||||
def pmc(request: FixtureRequest) -> Iterable[NDArray[numpy.float64] | None]:
|
def pmc(request: FixtureRequest) -> NDArray[numpy.float64] | None:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[(30, 1, 1),
|
@pytest.fixture(params=[(30, 1, 1),
|
||||||
(1, 30, 1),
|
(1, 30, 1),
|
||||||
(1, 1, 30)])
|
(1, 1, 30)])
|
||||||
def shape(request: FixtureRequest) -> Iterable[tuple[int, ...]]:
|
def shape(request: FixtureRequest) -> tuple[int, int, int]:
|
||||||
yield (3, *request.param)
|
return (3, *request.param)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[+1, -1])
|
@pytest.fixture(params=[+1, -1])
|
||||||
def src_polarity(request: FixtureRequest) -> Iterable[int]:
|
def src_polarity(request: FixtureRequest) -> int:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def j_distribution(
|
def j_distribution(
|
||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
epsilon: NDArray[numpy.float64],
|
epsilon: vfdfield,
|
||||||
dxes: dx_lists_mut,
|
dxes: dx_lists_mut,
|
||||||
omega: float,
|
omega: float,
|
||||||
src_polarity: int,
|
src_polarity: int,
|
||||||
) -> Iterable[NDArray[numpy.complex128]]:
|
) -> cfdfield_t:
|
||||||
j = numpy.zeros(shape, dtype=complex)
|
j = numpy.zeros(shape, dtype=complex)
|
||||||
|
|
||||||
dim = numpy.where(numpy.array(shape[1:]) > 1)[0][0] # Propagation axis
|
dim = numpy.where(numpy.array(shape[1:]) > 1)[0][0] # Propagation axis
|
||||||
@ -106,18 +106,18 @@ def j_distribution(
|
|||||||
|
|
||||||
j = fdfd.waveguide_3d.compute_source(E=e, wavenumber=wavenumber_corrected, omega=omega, dxes=dxes,
|
j = fdfd.waveguide_3d.compute_source(E=e, wavenumber=wavenumber_corrected, omega=omega, dxes=dxes,
|
||||||
axis=dim, polarity=src_polarity, slices=slices, epsilon=epsilon)
|
axis=dim, polarity=src_polarity, slices=slices, epsilon=epsilon)
|
||||||
yield j
|
return j
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def epsilon(
|
def epsilon(
|
||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
epsilon_bg: float,
|
epsilon_bg: float,
|
||||||
epsilon_fg: float,
|
epsilon_fg: float,
|
||||||
) -> Iterable[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64]:
|
||||||
epsilon = numpy.full(shape, epsilon_fg, dtype=float)
|
epsilon = numpy.full(shape, epsilon_fg, dtype=float)
|
||||||
yield epsilon
|
return epsilon
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['uniform'])
|
@pytest.fixture(params=['uniform'])
|
||||||
@ -127,7 +127,7 @@ def dxes(
|
|||||||
dx: float,
|
dx: float,
|
||||||
omega: float,
|
omega: float,
|
||||||
epsilon_fg: float,
|
epsilon_fg: float,
|
||||||
) -> Iterable[list[list[NDArray[numpy.float64]]]]:
|
) -> list[list[NDArray[numpy.float64]]]:
|
||||||
if request.param == 'uniform':
|
if request.param == 'uniform':
|
||||||
dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)]
|
dxes = [[numpy.full(s, dx) for s in shape[1:]] for _ in range(2)]
|
||||||
dim = numpy.where(numpy.array(shape[1:]) > 1)[0][0] # Propagation axis
|
dim = numpy.where(numpy.array(shape[1:]) > 1)[0][0] # Propagation axis
|
||||||
@ -141,10 +141,10 @@ def dxes(
|
|||||||
epsilon_effective=epsilon_fg,
|
epsilon_effective=epsilon_fg,
|
||||||
thickness=10,
|
thickness=10,
|
||||||
)
|
)
|
||||||
yield dxes
|
return dxes
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def sim(
|
def sim(
|
||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
@ -162,7 +162,7 @@ def sim(
|
|||||||
omega=omega,
|
omega=omega,
|
||||||
dxes=dxes,
|
dxes=dxes,
|
||||||
epsilon=eps_vec,
|
epsilon=eps_vec,
|
||||||
matrix_solver_opts={'atol': 1e-15, 'tol': 1e-11},
|
matrix_solver_opts={'atol': 1e-15, 'rtol': 1e-11},
|
||||||
)
|
)
|
||||||
e = unvec(e_vec, shape[1:])
|
e = unvec(e_vec, shape[1:])
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from typing import Iterable, Any
|
# ruff: noqa: ARG001
|
||||||
|
from typing import Any
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import pytest # type: ignore
|
import pytest # type: ignore
|
||||||
import numpy
|
import numpy
|
||||||
@ -150,8 +151,8 @@ def test_poynting_planes(sim: 'TDResult') -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[0.3])
|
@pytest.fixture(params=[0.3])
|
||||||
def dt(request: FixtureRequest) -> Iterable[float]:
|
def dt(request: FixtureRequest) -> float:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass()
|
@dataclasses.dataclass()
|
||||||
@ -168,8 +169,8 @@ class TDResult:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=[(0, 4, 8)]) # (0,)
|
@pytest.fixture(params=[(0, 4, 8)]) # (0,)
|
||||||
def j_steps(request: FixtureRequest) -> Iterable[tuple[int, ...]]:
|
def j_steps(request: FixtureRequest) -> tuple[int, ...]:
|
||||||
yield request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=['center', 'random'])
|
@pytest.fixture(params=['center', 'random'])
|
||||||
@ -177,7 +178,7 @@ def j_distribution(
|
|||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
j_mag: float,
|
j_mag: float,
|
||||||
) -> Iterable[NDArray[numpy.float64]]:
|
) -> NDArray[numpy.float64]:
|
||||||
j = numpy.zeros(shape)
|
j = numpy.zeros(shape)
|
||||||
if request.param == 'center':
|
if request.param == 'center':
|
||||||
j[:, shape[1] // 2, shape[2] // 2, shape[3] // 2] = j_mag
|
j[:, shape[1] // 2, shape[2] // 2, shape[3] // 2] = j_mag
|
||||||
@ -185,10 +186,10 @@ def j_distribution(
|
|||||||
j[:, 0, 0, 0] = j_mag
|
j[:, 0, 0, 0] = j_mag
|
||||||
elif request.param == 'random':
|
elif request.param == 'random':
|
||||||
j[:] = PRNG.uniform(low=-j_mag, high=j_mag, size=shape)
|
j[:] = PRNG.uniform(low=-j_mag, high=j_mag, size=shape)
|
||||||
yield j
|
return j
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def sim(
|
def sim(
|
||||||
request: FixtureRequest,
|
request: FixtureRequest,
|
||||||
shape: tuple[int, ...],
|
shape: tuple[int, ...],
|
||||||
@ -199,8 +200,7 @@ def sim(
|
|||||||
j_steps: tuple[int, ...],
|
j_steps: tuple[int, ...],
|
||||||
) -> TDResult:
|
) -> TDResult:
|
||||||
is3d = (numpy.array(shape) == 1).sum() == 0
|
is3d = (numpy.array(shape) == 1).sum() == 0
|
||||||
if is3d:
|
if is3d and dt != 0.3:
|
||||||
if dt != 0.3:
|
|
||||||
pytest.skip('Skipping dt != 0.3 because test is 3D (for speed)')
|
pytest.skip('Skipping dt != 0.3 because test is 3D (for speed)')
|
||||||
|
|
||||||
sim = TDResult(
|
sim = TDResult(
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
from typing import Any
|
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
@ -10,22 +8,25 @@ PRNG = numpy.random.RandomState(12345)
|
|||||||
def assert_fields_close(
|
def assert_fields_close(
|
||||||
x: NDArray,
|
x: NDArray,
|
||||||
y: NDArray,
|
y: NDArray,
|
||||||
*args: Any,
|
|
||||||
**kwargs: Any,
|
|
||||||
) -> None:
|
|
||||||
numpy.testing.assert_allclose(
|
|
||||||
x, y, verbose=False, # type: ignore
|
|
||||||
err_msg='Fields did not match:\n{}\n{}'.format(numpy.moveaxis(x, -1, 0),
|
|
||||||
numpy.moveaxis(y, -1, 0)),
|
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
x_disp = numpy.moveaxis(x, -1, 0)
|
||||||
|
y_disp = numpy.moveaxis(y, -1, 0)
|
||||||
|
numpy.testing.assert_allclose(
|
||||||
|
x, # type: ignore
|
||||||
|
y, # type: ignore
|
||||||
|
*args,
|
||||||
|
verbose=False,
|
||||||
|
err_msg=f'Fields did not match:\n{x_disp}\n{y_disp}',
|
||||||
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
def assert_close(
|
def assert_close(
|
||||||
x: NDArray,
|
x: NDArray,
|
||||||
y: NDArray,
|
y: NDArray,
|
||||||
*args: Any,
|
*args,
|
||||||
**kwargs: Any,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
numpy.testing.assert_allclose(x, y, *args, **kwargs)
|
numpy.testing.assert_allclose(x, y, *args, **kwargs)
|
||||||
|
|
||||||
|
|||||||
@ -39,9 +39,10 @@ include = [
|
|||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"numpy~=1.21",
|
"gridlock",
|
||||||
"scipy",
|
"numpy>=2.0",
|
||||||
]
|
"scipy~=1.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[tool.hatch.version]
|
[tool.hatch.version]
|
||||||
@ -49,5 +50,55 @@ path = "meanas/__init__.py"
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = ["pytest", "pdoc", "gridlock"]
|
dev = ["pytest", "pdoc", "gridlock"]
|
||||||
examples = ["gridlock"]
|
examples = [
|
||||||
|
"gridlock>=2.1",
|
||||||
|
"matplotlib>=3.10.8",
|
||||||
|
]
|
||||||
test = ["pytest"]
|
test = ["pytest"]
|
||||||
|
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
exclude = [
|
||||||
|
".git",
|
||||||
|
"dist",
|
||||||
|
]
|
||||||
|
line-length = 245
|
||||||
|
indent-width = 4
|
||||||
|
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||||
|
lint.select = [
|
||||||
|
"NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG",
|
||||||
|
"C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT",
|
||||||
|
"ARG", "PL", "R", "TRY",
|
||||||
|
"G010", "G101", "G201", "G202",
|
||||||
|
"Q002", "Q003", "Q004",
|
||||||
|
]
|
||||||
|
lint.ignore = [
|
||||||
|
#"ANN001", # No annotation
|
||||||
|
"ANN002", # *args
|
||||||
|
"ANN003", # **kwargs
|
||||||
|
"ANN401", # Any
|
||||||
|
"SIM108", # single-line if / else assignment
|
||||||
|
"RET504", # x=y+z; return x
|
||||||
|
"PIE790", # unnecessary pass
|
||||||
|
"ISC003", # non-implicit string concatenation
|
||||||
|
"C408", # dict(x=y) instead of {'x': y}
|
||||||
|
"PLR09", # Too many xxx
|
||||||
|
"PLR2004", # magic number
|
||||||
|
"PLC0414", # import x as x
|
||||||
|
"TRY003", # Long exception message
|
||||||
|
"TRY002", # Exception()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[[tool.mypy.overrides]]
|
||||||
|
module = [
|
||||||
|
"scipy",
|
||||||
|
"scipy.optimize",
|
||||||
|
"scipy.linalg",
|
||||||
|
"scipy.sparse",
|
||||||
|
"scipy.sparse.linalg",
|
||||||
|
]
|
||||||
|
ignore_missing_imports = true
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
gridlock = { path = "../gridlock", editable = true }
|
||||||
|
|||||||
806
uv.lock
generated
Normal file
806
uv.lock
generated
Normal file
@ -0,0 +1,806 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "contourpy"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cycler"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-raster"
|
||||||
|
version = "0.8"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "scipy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0d/7e/57d91306c966fc5ce8e068650741b599011a14e96ff0b8ec226668094287/float_raster-0.8.tar.gz", hash = "sha256:90e9c00d3908a8e0d50cd97c6df42055ac0fcf900302a504a4becaa024c60c22", size = 29233, upload-time = "2024-07-29T09:10:12.007Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/69/bcdcf1b52420d4b97ab6a18798a4ebb92292948b555a9a6782cb628821e6/float_raster-0.8-py3-none-any.whl", hash = "sha256:7e4ce9ffaf972e3ee788f16b06ee0eb07488b74634ee6f3db2402bf10ef29be7", size = 42469, upload-time = "2024-07-29T09:10:09.91Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fonttools"
|
||||||
|
version = "4.61.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/33/f9/0e84d593c0e12244150280a630999835a64f2852276161b62a0f98318de0/fonttools-4.61.0.tar.gz", hash = "sha256:ec520a1f0c7758d7a858a00f090c1745f6cde6a7c5e76fb70ea4044a15f712e7", size = 3561884, upload-time = "2025-11-28T17:05:49.491Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/be/5aa89cdddf2863d8afbdc19eb8ec5d8d35d40eeeb8e6cf52c5ff1c2dbd33/fonttools-4.61.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a32a16951cbf113d38f1dd8551b277b6e06e0f6f776fece0f99f746d739e1be3", size = 2847553, upload-time = "2025-11-28T17:04:30.539Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/3e/6ff643b07cead1236a534f51291ae2981721cf419135af5b740c002a66dd/fonttools-4.61.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328a9c227984bebaf69f3ac9062265f8f6acc7ddf2e4e344c63358579af0aa3d", size = 2388298, upload-time = "2025-11-28T17:04:32.161Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/15/fca8dfbe7b482e6f240b1aad0ed7c6e2e75e7a28efa3d3a03b570617b5e5/fonttools-4.61.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f0bafc8a3b3749c69cc610e5aa3da832d39c2a37a68f03d18ec9a02ecaac04a", size = 5054133, upload-time = "2025-11-28T17:04:34.035Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/a2/821c61c691b21fd09e07528a9a499cc2b075ac83ddb644aa16c9875a64bc/fonttools-4.61.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b5ca59b7417d149cf24e4c1933c9f44b2957424fc03536f132346d5242e0ebe5", size = 5031410, upload-time = "2025-11-28T17:04:36.141Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/f6/8b16339e93d03c732c8a23edefe3061b17a5f9107ddc47a3215ecd054cac/fonttools-4.61.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:df8cbce85cf482eb01f4551edca978c719f099c623277bda8332e5dbe7dba09d", size = 5030005, upload-time = "2025-11-28T17:04:38.314Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/eb/d4e150427bdaa147755239c931bbce829a88149ade5bfd8a327afe565567/fonttools-4.61.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7fb5b84f48a6a733ca3d7f41aa9551908ccabe8669ffe79586560abcc00a9cfd", size = 5154026, upload-time = "2025-11-28T17:04:40.34Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/5f/3dd00ce0dba6759943c707b1830af8c0bcf6f8f1a9fe46cb82e7ac2aaa74/fonttools-4.61.0-cp311-cp311-win32.whl", hash = "sha256:787ef9dfd1ea9fe49573c272412ae5f479d78e671981819538143bec65863865", size = 2276035, upload-time = "2025-11-28T17:04:42.59Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/44/798c472f096ddf12955eddb98f4f7c906e7497695d04ce073ddf7161d134/fonttools-4.61.0-cp311-cp311-win_amd64.whl", hash = "sha256:14fafda386377b6131d9e448af42d0926bad47e038de0e5ba1d58c25d621f028", size = 2327290, upload-time = "2025-11-28T17:04:44.57Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/5d/19e5939f773c7cb05480fe2e881d63870b63ee2b4bdb9a77d55b1d36c7b9/fonttools-4.61.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e24a1565c4e57111ec7f4915f8981ecbb61adf66a55f378fdc00e206059fcfef", size = 2846930, upload-time = "2025-11-28T17:04:46.639Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/b2/0658faf66f705293bd7e739a4f038302d188d424926be9c59bdad945664b/fonttools-4.61.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2bfacb5351303cae9f072ccf3fc6ecb437a6f359c0606bae4b1ab6715201d87", size = 2383016, upload-time = "2025-11-28T17:04:48.525Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/a3/1fa90b95b690f0d7541f48850adc40e9019374d896c1b8148d15012b2458/fonttools-4.61.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0bdcf2e29d65c26299cc3d502f4612365e8b90a939f46cd92d037b6cb7bb544a", size = 4949425, upload-time = "2025-11-28T17:04:50.482Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/af/00/acf18c00f6c501bd6e05ee930f926186f8a8e268265407065688820f1c94/fonttools-4.61.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6cd0d9051b8ddaf7385f99dd82ec2a058e2b46cf1f1961e68e1ff20fcbb61af", size = 4999632, upload-time = "2025-11-28T17:04:52.508Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/e0/19a2b86e54109b1d2ee8743c96a1d297238ae03243897bc5345c0365f34d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e074bc07c31406f45c418e17c1722e83560f181d122c412fa9e815df0ff74810", size = 4939438, upload-time = "2025-11-28T17:04:54.437Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/35/7b57a5f57d46286360355eff8d6b88c64ab6331107f37a273a71c803798d/fonttools-4.61.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a9b78da5d5faa17e63b2404b77feeae105c1b7e75f26020ab7a27b76e02039f", size = 5088960, upload-time = "2025-11-28T17:04:56.348Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/0e/6c5023eb2e0fe5d1ababc7e221e44acd3ff668781489cc1937a6f83d620a/fonttools-4.61.0-cp312-cp312-win32.whl", hash = "sha256:9821ed77bb676736b88fa87a737c97b6af06e8109667e625a4f00158540ce044", size = 2264404, upload-time = "2025-11-28T17:04:58.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/0b/63273128c7c5df19b1e4cd92e0a1e6ea5bb74a400c4905054c96ad60a675/fonttools-4.61.0-cp312-cp312-win_amd64.whl", hash = "sha256:0011d640afa61053bc6590f9a3394bd222de7cfde19346588beabac374e9d8ac", size = 2314427, upload-time = "2025-11-28T17:04:59.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/17/45/334f0d7f181e5473cfb757e1b60f4e60e7fc64f28d406e5d364a952718c0/fonttools-4.61.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba774b8cbd8754f54b8eb58124e8bd45f736b2743325ab1a5229698942b9b433", size = 2841801, upload-time = "2025-11-28T17:05:01.621Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/63/97b9c78e1f79bc741d4efe6e51f13872d8edb2b36e1b9fb2bab0d4491bb7/fonttools-4.61.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c84b430616ed73ce46e9cafd0bf0800e366a3e02fb7e1ad7c1e214dbe3862b1f", size = 2379024, upload-time = "2025-11-28T17:05:03.668Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/80/c87bc524a90dbeb2a390eea23eae448286983da59b7e02c67fa0ca96a8c5/fonttools-4.61.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b2b734d8391afe3c682320840c8191de9bd24e7eb85768dd4dc06ed1b63dbb1b", size = 4923706, upload-time = "2025-11-28T17:05:05.494Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/f6/a3b0374811a1de8c3f9207ec88f61ad1bb96f938ed89babae26c065c2e46/fonttools-4.61.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5c5fff72bf31b0e558ed085e4fd7ed96eb85881404ecc39ed2a779e7cf724eb", size = 4979751, upload-time = "2025-11-28T17:05:07.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/3b/30f63b4308b449091573285f9d27619563a84f399946bca3eadc9554afbe/fonttools-4.61.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:14a290c5c93fcab76b7f451e6a4b7721b712d90b3b5ed6908f1abcf794e90d6d", size = 4921113, upload-time = "2025-11-28T17:05:09.551Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/6c/58e6e9b7d9d8bf2d7010bd7bb493060b39b02a12d1cda64a8bfb116ce760/fonttools-4.61.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:13e3e20a5463bfeb77b3557d04b30bd6a96a6bb5c15c7b2e7908903e69d437a0", size = 5063183, upload-time = "2025-11-28T17:05:11.677Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3f/e3/52c790ab2b07492df059947a1fd7778e105aac5848c0473029a4d20481a2/fonttools-4.61.0-cp313-cp313-win32.whl", hash = "sha256:6781e7a4bb010be1cd69a29927b0305c86b843395f2613bdabe115f7d6ea7f34", size = 2263159, upload-time = "2025-11-28T17:05:13.292Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/1f/116013b200fbeba871046554d5d2a45fefa69a05c40e9cdfd0d4fff53edc/fonttools-4.61.0-cp313-cp313-win_amd64.whl", hash = "sha256:c53b47834ae41e8e4829171cc44fec0fdf125545a15f6da41776b926b9645a9a", size = 2313530, upload-time = "2025-11-28T17:05:14.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/99/59b1e25987787cb714aa9457cee4c9301b7c2153f0b673e2b8679d37669d/fonttools-4.61.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:96dfc9bc1f2302224e48e6ee37e656eddbab810b724b52e9d9c13a57a6abad01", size = 2841429, upload-time = "2025-11-28T17:05:16.671Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/b2/4c1911d4332c8a144bb3b44416e274ccca0e297157c971ea1b3fbb855590/fonttools-4.61.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3b2065d94e5d63aafc2591c8b6ccbdb511001d9619f1bca8ad39b745ebeb5efa", size = 2378987, upload-time = "2025-11-28T17:05:18.69Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/b0/f442e90fde5d2af2ae0cb54008ab6411edc557ee33b824e13e1d04925ac9/fonttools-4.61.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e0d87e81e4d869549585ba0beb3f033718501c1095004f5e6aef598d13ebc216", size = 4873270, upload-time = "2025-11-28T17:05:20.625Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/04/f5d5990e33053c8a59b90b1d7e10ad9b97a73f42c745304da0e709635fab/fonttools-4.61.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cfa2eb9bae650e58f0e8ad53c49d19a844d6034d6b259f30f197238abc1ccee", size = 4968270, upload-time = "2025-11-28T17:05:22.515Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/9f/2091402e0d27c9c8c4bab5de0e5cd146d9609a2d7d1c666bbb75c0011c1a/fonttools-4.61.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4238120002e68296d55e091411c09eab94e111c8ce64716d17df53fd0eb3bb3d", size = 4919799, upload-time = "2025-11-28T17:05:24.437Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/72/86adab22fde710b829f8ffbc8f264df01928e5b7a8f6177fa29979ebf256/fonttools-4.61.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b6ceac262cc62bec01b3bb59abccf41b24ef6580869e306a4e88b7e56bb4bdda", size = 5030966, upload-time = "2025-11-28T17:05:26.115Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/a7/7c8e31b003349e845b853f5e0a67b95ff6b052fa4f5224f8b72624f5ac69/fonttools-4.61.0-cp314-cp314-win32.whl", hash = "sha256:adbb4ecee1a779469a77377bbe490565effe8fce6fb2e6f95f064de58f8bac85", size = 2267243, upload-time = "2025-11-28T17:05:27.807Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/ee/f434fe7749360497c52b7dcbcfdbccdaab0a71c59f19d572576066717122/fonttools-4.61.0-cp314-cp314-win_amd64.whl", hash = "sha256:02bdf8e04d1a70476564b8640380f04bb4ac74edc1fc71f1bacb840b3e398ee9", size = 2318822, upload-time = "2025-11-28T17:05:29.882Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/b3/c16255320255e5c1863ca2b2599bb61a46e2f566db0bbb9948615a8fe692/fonttools-4.61.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:627216062d90ab0d98215176d8b9562c4dd5b61271d35f130bcd30f6a8aaa33a", size = 2924917, upload-time = "2025-11-28T17:05:31.46Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/b8/08067ae21de705a817777c02ef36ab0b953cbe91d8adf134f9c2da75ed6d/fonttools-4.61.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7b446623c9cd5f14a59493818eaa80255eec2468c27d2c01b56e05357c263195", size = 2413576, upload-time = "2025-11-28T17:05:33.343Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/42/f1/96ff43f92addce2356780fdc203f2966206f3d22ea20e242c27826fd7442/fonttools-4.61.0-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:70e2a0c0182ee75e493ef33061bfebf140ea57e035481d2f95aa03b66c7a0e05", size = 4877447, upload-time = "2025-11-28T17:05:35.278Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/1e/a3d8e51ed9ccfd7385e239ae374b78d258a0fb82d82cab99160a014a45d1/fonttools-4.61.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9064b0f55b947e929ac669af5311ab1f26f750214db6dd9a0c97e091e918f486", size = 5095681, upload-time = "2025-11-28T17:05:37.142Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/eb/f6/d256bd6c1065c146a0bdddf1c62f542e08ae5b3405dbf3fcc52be272f674/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5e45a824ce14b90510024d0d39dae51bd4fbb54c42a9334ea8c8cf4d95cbe", size = 4974140, upload-time = "2025-11-28T17:05:39.5Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/0c/96633eb4b26f138cc48561c6e0c44b4ea48acea56b20b507d6b14f8e80ce/fonttools-4.61.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6e5ca8c62efdec7972dfdfd454415c4db49b89aeaefaaacada432f3b7eea9866", size = 5001741, upload-time = "2025-11-28T17:05:41.424Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/9a/3b536bad3be4f26186f296e749ff17bad3e6d57232c104d752d24b2e265b/fonttools-4.61.0-cp314-cp314t-win32.whl", hash = "sha256:63c7125d31abe3e61d7bb917329b5543c5b3448db95f24081a13aaf064360fc8", size = 2330707, upload-time = "2025-11-28T17:05:43.548Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/ea/e6b9ac610451ee9f04477c311ad126de971f6112cb579fa391d2a8edb00b/fonttools-4.61.0-cp314-cp314t-win_amd64.whl", hash = "sha256:67d841aa272be5500de7f447c40d1d8452783af33b4c3599899319f6ef9ad3c1", size = 2395950, upload-time = "2025-11-28T17:05:45.638Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/14/634f7daea5ffe6a5f7a0322ba8e1a0e23c9257b80aa91458107896d1dfc7/fonttools-4.61.0-py3-none-any.whl", hash = "sha256:276f14c560e6f98d24ef7f5f44438e55ff5a67f78fa85236b218462c9f5d0635", size = 1144485, upload-time = "2025-11-28T17:05:47.573Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gridlock"
|
||||||
|
source = { editable = "../gridlock" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "float-raster" },
|
||||||
|
{ name = "numpy" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "float-raster", specifier = ">=0.8" },
|
||||||
|
{ name = "matplotlib", marker = "extra == 'visualization'" },
|
||||||
|
{ name = "matplotlib", marker = "extra == 'visualization-isosurface'" },
|
||||||
|
{ name = "mpl-toolkits", marker = "extra == 'visualization-isosurface'" },
|
||||||
|
{ name = "numpy", specifier = ">=1.26" },
|
||||||
|
{ name = "skimage", marker = "extra == 'visualization-isosurface'", specifier = ">=0.13" },
|
||||||
|
]
|
||||||
|
provides-extras = ["visualization", "visualization-isosurface"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jinja2"
|
||||||
|
version = "3.1.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markupsafe" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kiwisolver"
|
||||||
|
version = "1.4.9"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown2"
|
||||||
|
version = "2.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/42/f8/b2ae8bf5f28f9b510ae097415e6e4cb63226bb28d7ee01aec03a755ba03b/markdown2-2.5.4.tar.gz", hash = "sha256:a09873f0b3c23dbfae589b0080587df52ad75bb09a5fa6559147554736676889", size = 145652, upload-time = "2025-07-27T16:16:24.307Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/06/2697b5043c3ecb720ce0d243fc7cf5024c0b5b1e450506e9b21939019963/markdown2-2.5.4-py3-none-any.whl", hash = "sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439", size = 49954, upload-time = "2025-07-27T16:16:23.026Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markupsafe"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matplotlib"
|
||||||
|
version = "3.10.8"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "contourpy" },
|
||||||
|
{ name = "cycler" },
|
||||||
|
{ name = "fonttools" },
|
||||||
|
{ name = "kiwisolver" },
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "pyparsing" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meanas"
|
||||||
|
source = { editable = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "gridlock" },
|
||||||
|
{ name = "numpy" },
|
||||||
|
{ name = "scipy" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
{ name = "gridlock" },
|
||||||
|
{ name = "pdoc" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
examples = [
|
||||||
|
{ name = "gridlock" },
|
||||||
|
{ name = "matplotlib" },
|
||||||
|
]
|
||||||
|
test = [
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "gridlock", editable = "../gridlock" },
|
||||||
|
{ name = "gridlock", marker = "extra == 'dev'", editable = "../gridlock" },
|
||||||
|
{ name = "gridlock", marker = "extra == 'examples'", editable = "../gridlock" },
|
||||||
|
{ name = "matplotlib", marker = "extra == 'examples'", specifier = ">=3.10.8" },
|
||||||
|
{ name = "numpy", specifier = ">=2.0" },
|
||||||
|
{ name = "pdoc", marker = "extra == 'dev'" },
|
||||||
|
{ name = "pytest", marker = "extra == 'dev'" },
|
||||||
|
{ name = "pytest", marker = "extra == 'test'" },
|
||||||
|
{ name = "scipy", specifier = "~=1.14" },
|
||||||
|
]
|
||||||
|
provides-extras = ["dev", "examples", "test"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numpy"
|
||||||
|
version = "2.3.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/76/65/21b3bc86aac7b8f2862db1e808f1ea22b028e30a225a34a5ede9bf8678f2/numpy-2.3.5.tar.gz", hash = "sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0", size = 20584950, upload-time = "2025-11-16T22:52:42.067Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/77/84dd1d2e34d7e2792a236ba180b5e8fcc1e3e414e761ce0253f63d7f572e/numpy-2.3.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10", size = 17034641, upload-time = "2025-11-16T22:49:19.336Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/ea/25e26fa5837106cde46ae7d0b667e20f69cbbc0efd64cba8221411ab26ae/numpy-2.3.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218", size = 12528324, upload-time = "2025-11-16T22:49:22.582Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/1a/e85f0eea4cf03d6a0228f5c0256b53f2df4bc794706e7df019fc622e47f1/numpy-2.3.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d", size = 5356872, upload-time = "2025-11-16T22:49:25.408Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/bb/35ef04afd567f4c989c2060cde39211e4ac5357155c1833bcd1166055c61/numpy-2.3.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5", size = 6893148, upload-time = "2025-11-16T22:49:27.549Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/2b/05bbeb06e2dff5eab512dfc678b1cc5ee94d8ac5956a0885c64b6b26252b/numpy-2.3.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7", size = 14557282, upload-time = "2025-11-16T22:49:30.964Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/fb/2b23769462b34398d9326081fad5655198fcf18966fcb1f1e49db44fbf31/numpy-2.3.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4", size = 16897903, upload-time = "2025-11-16T22:49:34.191Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/14/085f4cf05fc3f1e8aa95e85404e984ffca9b2275a5dc2b1aae18a67538b8/numpy-2.3.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e", size = 16341672, upload-time = "2025-11-16T22:49:37.2Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/3b/1f73994904142b2aa290449b3bb99772477b5fd94d787093e4f24f5af763/numpy-2.3.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748", size = 18838896, upload-time = "2025-11-16T22:49:39.727Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/b9/cf6649b2124f288309ffc353070792caf42ad69047dcc60da85ee85fea58/numpy-2.3.5-cp311-cp311-win32.whl", hash = "sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c", size = 6563608, upload-time = "2025-11-16T22:49:42.079Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/44/9fe81ae1dcc29c531843852e2874080dc441338574ccc4306b39e2ff6e59/numpy-2.3.5-cp311-cp311-win_amd64.whl", hash = "sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c", size = 13078442, upload-time = "2025-11-16T22:49:43.99Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/a7/f99a41553d2da82a20a2f22e93c94f928e4490bb447c9ff3c4ff230581d3/numpy-2.3.5-cp311-cp311-win_arm64.whl", hash = "sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa", size = 10458555, upload-time = "2025-11-16T22:49:47.092Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/37/e669fe6cbb2b96c62f6bbedc6a81c0f3b7362f6a59230b23caa673a85721/numpy-2.3.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e", size = 16733873, upload-time = "2025-11-16T22:49:49.84Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/65/df0db6c097892c9380851ab9e44b52d4f7ba576b833996e0080181c0c439/numpy-2.3.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769", size = 12259838, upload-time = "2025-11-16T22:49:52.863Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5b/e1/1ee06e70eb2136797abe847d386e7c0e830b67ad1d43f364dd04fa50d338/numpy-2.3.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5", size = 5088378, upload-time = "2025-11-16T22:49:55.055Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/9c/1ca85fb86708724275103b81ec4cf1ac1d08f465368acfc8da7ab545bdae/numpy-2.3.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4", size = 6628559, upload-time = "2025-11-16T22:49:57.371Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/78/fcd41e5a0ce4f3f7b003da85825acddae6d7ecb60cf25194741b036ca7d6/numpy-2.3.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d", size = 14250702, upload-time = "2025-11-16T22:49:59.632Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/23/2a1b231b8ff672b4c450dac27164a8b2ca7d9b7144f9c02d2396518352eb/numpy-2.3.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28", size = 16606086, upload-time = "2025-11-16T22:50:02.127Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/c5/5ad26fbfbe2012e190cc7d5003e4d874b88bb18861d0829edc140a713021/numpy-2.3.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b", size = 16025985, upload-time = "2025-11-16T22:50:04.536Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/fa/dd48e225c46c819288148d9d060b047fd2a6fb1eb37eae25112ee4cb4453/numpy-2.3.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c", size = 18542976, upload-time = "2025-11-16T22:50:07.557Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/79/ccbd23a75862d95af03d28b5c6901a1b7da4803181513d52f3b86ed9446e/numpy-2.3.5-cp312-cp312-win32.whl", hash = "sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952", size = 6285274, upload-time = "2025-11-16T22:50:10.746Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/57/8aeaf160312f7f489dea47ab61e430b5cb051f59a98ae68b7133ce8fa06a/numpy-2.3.5-cp312-cp312-win_amd64.whl", hash = "sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa", size = 12782922, upload-time = "2025-11-16T22:50:12.811Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/a6/aae5cc2ca78c45e64b9ef22f089141d661516856cf7c8a54ba434576900d/numpy-2.3.5-cp312-cp312-win_arm64.whl", hash = "sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013", size = 10194667, upload-time = "2025-11-16T22:50:16.16Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/69/9cde09f36da4b5a505341180a3f2e6fadc352fd4d2b7096ce9778db83f1a/numpy-2.3.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff", size = 16728251, upload-time = "2025-11-16T22:50:19.013Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/fb/f505c95ceddd7027347b067689db71ca80bd5ecc926f913f1a23e65cf09b/numpy-2.3.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188", size = 12254652, upload-time = "2025-11-16T22:50:21.487Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/da/8c7738060ca9c31b30e9301ee0cf6c5ffdbf889d9593285a1cead337f9a5/numpy-2.3.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0", size = 5083172, upload-time = "2025-11-16T22:50:24.562Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/b4/ee5bb2537fb9430fd2ef30a616c3672b991a4129bb1c7dcc42aa0abbe5d7/numpy-2.3.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903", size = 6622990, upload-time = "2025-11-16T22:50:26.47Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/03/dc0723a013c7d7c19de5ef29e932c3081df1c14ba582b8b86b5de9db7f0f/numpy-2.3.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d", size = 14248902, upload-time = "2025-11-16T22:50:28.861Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/10/ca162f45a102738958dcec8023062dad0cbc17d1ab99d68c4e4a6c45fb2b/numpy-2.3.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017", size = 16597430, upload-time = "2025-11-16T22:50:31.56Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2a/51/c1e29be863588db58175175f057286900b4b3327a1351e706d5e0f8dd679/numpy-2.3.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf", size = 16024551, upload-time = "2025-11-16T22:50:34.242Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/68/8236589d4dbb87253d28259d04d9b814ec0ecce7cb1c7fed29729f4c3a78/numpy-2.3.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce", size = 18533275, upload-time = "2025-11-16T22:50:37.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/56/2932d75b6f13465239e3b7b7e511be27f1b8161ca2510854f0b6e521c395/numpy-2.3.5-cp313-cp313-win32.whl", hash = "sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e", size = 6277637, upload-time = "2025-11-16T22:50:40.11Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/88/e2eaa6cffb115b85ed7c7c87775cb8bcf0816816bc98ca8dbfa2ee33fe6e/numpy-2.3.5-cp313-cp313-win_amd64.whl", hash = "sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b", size = 12779090, upload-time = "2025-11-16T22:50:42.503Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/88/3f41e13a44ebd4034ee17baa384acac29ba6a4fcc2aca95f6f08ca0447d1/numpy-2.3.5-cp313-cp313-win_arm64.whl", hash = "sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae", size = 10194710, upload-time = "2025-11-16T22:50:44.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/cb/71744144e13389d577f867f745b7df2d8489463654a918eea2eeb166dfc9/numpy-2.3.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd", size = 16827292, upload-time = "2025-11-16T22:50:47.715Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/80/ba9dc6f2a4398e7f42b708a7fdc841bb638d353be255655498edbf9a15a8/numpy-2.3.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f", size = 12378897, upload-time = "2025-11-16T22:50:51.327Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/6d/db2151b9f64264bcceccd51741aa39b50150de9b602d98ecfe7e0c4bff39/numpy-2.3.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a", size = 5207391, upload-time = "2025-11-16T22:50:54.542Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/ae/429bacace5ccad48a14c4ae5332f6aa8ab9f69524193511d60ccdfdc65fa/numpy-2.3.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139", size = 6721275, upload-time = "2025-11-16T22:50:56.794Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/5b/1919abf32d8722646a38cd527bc3771eb229a32724ee6ba340ead9b92249/numpy-2.3.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e", size = 14306855, upload-time = "2025-11-16T22:50:59.208Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/87/6831980559434973bebc30cd9c1f21e541a0f2b0c280d43d3afd909b66d0/numpy-2.3.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9", size = 16657359, upload-time = "2025-11-16T22:51:01.991Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/91/c797f544491ee99fd00495f12ebb7802c440c1915811d72ac5b4479a3356/numpy-2.3.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946", size = 16093374, upload-time = "2025-11-16T22:51:05.291Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/a6/54da03253afcbe7a72785ec4da9c69fb7a17710141ff9ac5fcb2e32dbe64/numpy-2.3.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1", size = 18594587, upload-time = "2025-11-16T22:51:08.585Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/e9/aff53abbdd41b0ecca94285f325aff42357c6b5abc482a3fcb4994290b18/numpy-2.3.5-cp313-cp313t-win32.whl", hash = "sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3", size = 6405940, upload-time = "2025-11-16T22:51:11.541Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/81/50613fec9d4de5480de18d4f8ef59ad7e344d497edbef3cfd80f24f98461/numpy-2.3.5-cp313-cp313t-win_amd64.whl", hash = "sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234", size = 12920341, upload-time = "2025-11-16T22:51:14.312Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/ab/08fd63b9a74303947f34f0bd7c5903b9c5532c2d287bead5bdf4c556c486/numpy-2.3.5-cp313-cp313t-win_arm64.whl", hash = "sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7", size = 10262507, upload-time = "2025-11-16T22:51:16.846Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/97/1a914559c19e32d6b2e233cf9a6a114e67c856d35b1d6babca571a3e880f/numpy-2.3.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82", size = 16735706, upload-time = "2025-11-16T22:51:19.558Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/d4/51233b1c1b13ecd796311216ae417796b88b0616cfd8a33ae4536330748a/numpy-2.3.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0", size = 12264507, upload-time = "2025-11-16T22:51:22.492Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/98/2fe46c5c2675b8306d0b4a3ec3494273e93e1226a490f766e84298576956/numpy-2.3.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63", size = 5093049, upload-time = "2025-11-16T22:51:25.171Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/0e/0698378989bb0ac5f1660c81c78ab1fe5476c1a521ca9ee9d0710ce54099/numpy-2.3.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9", size = 6626603, upload-time = "2025-11-16T22:51:27Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/a6/9ca0eecc489640615642a6cbc0ca9e10df70df38c4d43f5a928ff18d8827/numpy-2.3.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b", size = 14262696, upload-time = "2025-11-16T22:51:29.402Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c8/f6/07ec185b90ec9d7217a00eeeed7383b73d7e709dae2a9a021b051542a708/numpy-2.3.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520", size = 16597350, upload-time = "2025-11-16T22:51:32.167Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/37/164071d1dde6a1a84c9b8e5b414fa127981bad47adf3a6b7e23917e52190/numpy-2.3.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c", size = 16040190, upload-time = "2025-11-16T22:51:35.403Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/3c/f18b82a406b04859eb026d204e4e1773eb41c5be58410f41ffa511d114ae/numpy-2.3.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8", size = 18536749, upload-time = "2025-11-16T22:51:39.698Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/79/f82f572bf44cf0023a2fe8588768e23e1592585020d638999f15158609e1/numpy-2.3.5-cp314-cp314-win32.whl", hash = "sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248", size = 6335432, upload-time = "2025-11-16T22:51:42.476Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/2e/235b4d96619931192c91660805e5e49242389742a7a82c27665021db690c/numpy-2.3.5-cp314-cp314-win_amd64.whl", hash = "sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e", size = 12919388, upload-time = "2025-11-16T22:51:45.275Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/2b/29fd75ce45d22a39c61aad74f3d718e7ab67ccf839ca8b60866054eb15f8/numpy-2.3.5-cp314-cp314-win_arm64.whl", hash = "sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2", size = 10476651, upload-time = "2025-11-16T22:51:47.749Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/17/e1/f6a721234ebd4d87084cfa68d081bcba2f5cfe1974f7de4e0e8b9b2a2ba1/numpy-2.3.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41", size = 16834503, upload-time = "2025-11-16T22:51:50.443Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/1c/baf7ffdc3af9c356e1c135e57ab7cf8d247931b9554f55c467efe2c69eff/numpy-2.3.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad", size = 12381612, upload-time = "2025-11-16T22:51:53.609Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/74/91/f7f0295151407ddc9ba34e699013c32c3c91944f9b35fcf9281163dc1468/numpy-2.3.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39", size = 5210042, upload-time = "2025-11-16T22:51:56.213Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/3b/78aebf345104ec50dd50a4d06ddeb46a9ff5261c33bcc58b1c4f12f85ec2/numpy-2.3.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20", size = 6724502, upload-time = "2025-11-16T22:51:58.584Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/c6/7c34b528740512e57ef1b7c8337ab0b4f0bddf34c723b8996c675bc2bc91/numpy-2.3.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52", size = 14308962, upload-time = "2025-11-16T22:52:01.698Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/35/09d433c5262bc32d725bafc619e095b6a6651caf94027a03da624146f655/numpy-2.3.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b", size = 16655054, upload-time = "2025-11-16T22:52:04.267Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7a/ab/6a7b259703c09a88804fa2430b43d6457b692378f6b74b356155283566ac/numpy-2.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3", size = 16091613, upload-time = "2025-11-16T22:52:08.651Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/88/330da2071e8771e60d1038166ff9d73f29da37b01ec3eb43cb1427464e10/numpy-2.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227", size = 18591147, upload-time = "2025-11-16T22:52:11.453Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/41/851c4b4082402d9ea860c3626db5d5df47164a712cb23b54be028b184c1c/numpy-2.3.5-cp314-cp314t-win32.whl", hash = "sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5", size = 6479806, upload-time = "2025-11-16T22:52:14.641Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/30/d48bde1dfd93332fa557cff1972fbc039e055a52021fbef4c2c4b1eefd17/numpy-2.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf", size = 13105760, upload-time = "2025-11-16T22:52:17.975Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/65/f9dea8e109371ade9c782b4e4756a82edf9d3366bca495d84d79859a0b79/numpy-2.3.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310", size = 16910689, upload-time = "2025-11-16T22:52:23.247Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/4f/edb00032a8fb92ec0a679d3830368355da91a69cab6f3e9c21b64d0bb986/numpy-2.3.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c", size = 12457053, upload-time = "2025-11-16T22:52:26.367Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/a4/e8a53b5abd500a63836a29ebe145fc1ab1f2eefe1cfe59276020373ae0aa/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18", size = 5285635, upload-time = "2025-11-16T22:52:29.266Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/2f/37eeb9014d9c8b3e9c55bc599c68263ca44fdbc12a93e45a21d1d56df737/numpy-2.3.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff", size = 6801770, upload-time = "2025-11-16T22:52:31.421Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/e4/68d2f474df2cb671b2b6c2986a02e520671295647dad82484cde80ca427b/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb", size = 14391768, upload-time = "2025-11-16T22:52:33.593Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/50/94ccd8a2b141cb50651fddd4f6a48874acb3c91c8f0842b08a6afc4b0b21/numpy-2.3.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7", size = 16729263, upload-time = "2025-11-16T22:52:36.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/ee/346fa473e666fe14c52fcdd19ec2424157290a032d4c41f98127bfb31ac7/numpy-2.3.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425", size = 12967213, upload-time = "2025-11-16T22:52:39.38Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "25.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pdoc"
|
||||||
|
version = "16.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "jinja2" },
|
||||||
|
{ name = "markdown2" },
|
||||||
|
{ name = "markupsafe" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ac/fe/ab3f34a5fb08c6b698439a2c2643caf8fef0d61a86dd3fdcd5501c670ab8/pdoc-16.0.0.tar.gz", hash = "sha256:fdadc40cc717ec53919e3cd720390d4e3bcd40405cb51c4918c119447f913514", size = 111890, upload-time = "2025-10-27T16:02:16.345Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/a1/56a17b7f9e18c2bb8df73f3833345d97083b344708b97bab148fdd7e0b82/pdoc-16.0.0-py3-none-any.whl", hash = "sha256:070b51de2743b9b1a4e0ab193a06c9e6c12cf4151cf9137656eebb16e8556628", size = 100014, upload-time = "2025-10-27T16:02:15.007Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pillow"
|
||||||
|
version = "12.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/5a/a2f6773b64edb921a756eb0729068acad9fc5208a53f4a349396e9436721/pillow-12.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0fd00cac9c03256c8b2ff58f162ebcd2587ad3e1f2e397eab718c47e24d231cc", size = 5289798, upload-time = "2025-10-15T18:21:47.763Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/05/069b1f8a2e4b5a37493da6c5868531c3f77b85e716ad7a590ef87d58730d/pillow-12.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3475b96f5908b3b16c47533daaa87380c491357d197564e0ba34ae75c0f3257", size = 4650589, upload-time = "2025-10-15T18:21:49.515Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/e3/2c820d6e9a36432503ead175ae294f96861b07600a7156154a086ba7111a/pillow-12.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:110486b79f2d112cf6add83b28b627e369219388f64ef2f960fef9ebaf54c642", size = 6230472, upload-time = "2025-10-15T18:21:51.052Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/89/63427f51c64209c5e23d4d52071c8d0f21024d3a8a487737caaf614a5795/pillow-12.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5269cc1caeedb67e6f7269a42014f381f45e2e7cd42d834ede3c703a1d915fe3", size = 8033887, upload-time = "2025-10-15T18:21:52.604Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/1b/c9711318d4901093c15840f268ad649459cd81984c9ec9887756cca049a5/pillow-12.0.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa5129de4e174daccbc59d0a3b6d20eaf24417d59851c07ebb37aeb02947987c", size = 6343964, upload-time = "2025-10-15T18:21:54.619Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/41/1e/db9470f2d030b4995083044cd8738cdd1bf773106819f6d8ba12597d5352/pillow-12.0.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bee2a6db3a7242ea309aa7ee8e2780726fed67ff4e5b40169f2c940e7eb09227", size = 7034756, upload-time = "2025-10-15T18:21:56.151Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/b0/6177a8bdd5ee4ed87cba2de5a3cc1db55ffbbec6176784ce5bb75aa96798/pillow-12.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:90387104ee8400a7b4598253b4c406f8958f59fcf983a6cea2b50d59f7d63d0b", size = 6458075, upload-time = "2025-10-15T18:21:57.759Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/5e/61537aa6fa977922c6a03253a0e727e6e4a72381a80d63ad8eec350684f2/pillow-12.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc91a56697869546d1b8f0a3ff35224557ae7f881050e99f615e0119bf934b4e", size = 7125955, upload-time = "2025-10-15T18:21:59.372Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/3d/d5033539344ee3cbd9a4d69e12e63ca3a44a739eb2d4c8da350a3d38edd7/pillow-12.0.0-cp311-cp311-win32.whl", hash = "sha256:27f95b12453d165099c84f8a8bfdfd46b9e4bda9e0e4b65f0635430027f55739", size = 6298440, upload-time = "2025-10-15T18:22:00.982Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/42/aaca386de5cc8bd8a0254516957c1f265e3521c91515b16e286c662854c4/pillow-12.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:b583dc9070312190192631373c6c8ed277254aa6e6084b74bdd0a6d3b221608e", size = 6999256, upload-time = "2025-10-15T18:22:02.617Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/f1/9197c9c2d5708b785f631a6dfbfa8eb3fb9672837cb92ae9af812c13b4ed/pillow-12.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:759de84a33be3b178a64c8ba28ad5c135900359e85fb662bc6e403ad4407791d", size = 2436025, upload-time = "2025-10-15T18:22:04.598Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/90/4fcce2c22caf044e660a198d740e7fbc14395619e3cb1abad12192c0826c/pillow-12.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:53561a4ddc36facb432fae7a9d8afbfaf94795414f5cdc5fc52f28c1dca90371", size = 5249377, upload-time = "2025-10-15T18:22:05.993Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/e0/ed960067543d080691d47d6938ebccbf3976a931c9567ab2fbfab983a5dd/pillow-12.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:71db6b4c1653045dacc1585c1b0d184004f0d7e694c7b34ac165ca70c0838082", size = 4650343, upload-time = "2025-10-15T18:22:07.718Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/a1/f81fdeddcb99c044bf7d6faa47e12850f13cee0849537a7d27eeab5534d4/pillow-12.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2fa5f0b6716fc88f11380b88b31fe591a06c6315e955c096c35715788b339e3f", size = 6232981, upload-time = "2025-10-15T18:22:09.287Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/e1/9098d3ce341a8750b55b0e00c03f1630d6178f38ac191c81c97a3b047b44/pillow-12.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82240051c6ca513c616f7f9da06e871f61bfd7805f566275841af15015b8f98d", size = 8041399, upload-time = "2025-10-15T18:22:10.872Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/62/a22e8d3b602ae8cc01446d0c57a54e982737f44b6f2e1e019a925143771d/pillow-12.0.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55f818bd74fe2f11d4d7cbc65880a843c4075e0ac7226bc1a23261dbea531953", size = 6347740, upload-time = "2025-10-15T18:22:12.769Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/87/424511bdcd02c8d7acf9f65caa09f291a519b16bd83c3fb3374b3d4ae951/pillow-12.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b87843e225e74576437fd5b6a4c2205d422754f84a06942cfaf1dc32243e45a8", size = 7040201, upload-time = "2025-10-15T18:22:14.813Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/4d/435c8ac688c54d11755aedfdd9f29c9eeddf68d150fe42d1d3dbd2365149/pillow-12.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c607c90ba67533e1b2355b821fef6764d1dd2cbe26b8c1005ae84f7aea25ff79", size = 6462334, upload-time = "2025-10-15T18:22:16.375Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/f2/ad34167a8059a59b8ad10bc5c72d4d9b35acc6b7c0877af8ac885b5f2044/pillow-12.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21f241bdd5080a15bc86d3466a9f6074a9c2c2b314100dd896ac81ee6db2f1ba", size = 7134162, upload-time = "2025-10-15T18:22:17.996Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/b1/a7391df6adacf0a5c2cf6ac1cf1fcc1369e7d439d28f637a847f8803beb3/pillow-12.0.0-cp312-cp312-win32.whl", hash = "sha256:dd333073e0cacdc3089525c7df7d39b211bcdf31fc2824e49d01c6b6187b07d0", size = 6298769, upload-time = "2025-10-15T18:22:19.923Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/0b/d87733741526541c909bbf159e338dcace4f982daac6e5a8d6be225ca32d/pillow-12.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:9fe611163f6303d1619bbcb653540a4d60f9e55e622d60a3108be0d5b441017a", size = 7001107, upload-time = "2025-10-15T18:22:21.644Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/96/aaa61ce33cc98421fb6088af2a03be4157b1e7e0e87087c888e2370a7f45/pillow-12.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:7dfb439562f234f7d57b1ac6bc8fe7f838a4bd49c79230e0f6a1da93e82f1fad", size = 2436012, upload-time = "2025-10-15T18:22:23.621Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1d/b3/582327e6c9f86d037b63beebe981425d6811104cb443e8193824ef1a2f27/pillow-12.0.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b22bd8c974942477156be55a768f7aa37c46904c175be4e158b6a86e3a6b7ca8", size = 5215068, upload-time = "2025-10-15T18:23:59.594Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/d6/67748211d119f3b6540baf90f92fae73ae51d5217b171b0e8b5f7e5d558f/pillow-12.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:805ebf596939e48dbb2e4922a1d3852cfc25c38160751ce02da93058b48d252a", size = 4614994, upload-time = "2025-10-15T18:24:01.669Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2d/e1/f8281e5d844c41872b273b9f2c34a4bf64ca08905668c8ae730eedc7c9fa/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae81479f77420d217def5f54b5b9d279804d17e982e0f2fa19b1d1e14ab5197", size = 5246639, upload-time = "2025-10-15T18:24:03.403Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/5a/0d8ab8ffe8a102ff5df60d0de5af309015163bf710c7bb3e8311dd3b3ad0/pillow-12.0.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeaefa96c768fc66818730b952a862235d68825c178f1b3ffd4efd7ad2edcb7c", size = 6986839, upload-time = "2025-10-15T18:24:05.344Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/2e/3434380e8110b76cd9eb00a363c484b050f949b4bbe84ba770bb8508a02c/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09f2d0abef9e4e2f349305a4f8cc784a8a6c2f58a8c4892eea13b10a943bd26e", size = 5313505, upload-time = "2025-10-15T18:24:07.137Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/ca/5a9d38900d9d74785141d6580950fe705de68af735ff6e727cb911b64740/pillow-12.0.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdee52571a343d721fb2eb3b090a82d959ff37fc631e3f70422e0c2e029f3e76", size = 5963654, upload-time = "2025-10-15T18:24:09.579Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/7e/f896623c3c635a90537ac093c6a618ebe1a90d87206e42309cb5d98a1b9e/pillow-12.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b290fd8aa38422444d4b50d579de197557f182ef1068b75f5aa8558638b8d0a5", size = 6997850, upload-time = "2025-10-15T18:24:11.495Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pygments"
|
||||||
|
version = "2.19.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyparsing"
|
||||||
|
version = "3.2.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f2/a5/181488fc2b9d093e3972d2a472855aae8a03f000592dbfce716a512b3359/pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6", size = 1099274, upload-time = "2025-09-21T04:11:06.277Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/5e/1aa9a93198c6b64513c9d7752de7422c06402de6600a8767da1524f9570b/pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e", size = 113890, upload-time = "2025-09-21T04:11:04.117Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "9.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.9.0.post0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scipy"
|
||||||
|
version = "1.16.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c", size = 36617856, upload-time = "2025-10-28T17:33:31.375Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d", size = 28874306, upload-time = "2025-10-28T17:33:36.516Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/15/65/3a9400efd0228a176e6ec3454b1fa998fbbb5a8defa1672c3f65706987db/scipy-1.16.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9", size = 20865371, upload-time = "2025-10-28T17:33:42.094Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/d7/eda09adf009a9fb81827194d4dd02d2e4bc752cef16737cc4ef065234031/scipy-1.16.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4", size = 23524877, upload-time = "2025-10-28T17:33:48.483Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7d/6b/3f911e1ebc364cb81320223a3422aab7d26c9c7973109a9cd0f27c64c6c0/scipy-1.16.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959", size = 33342103, upload-time = "2025-10-28T17:33:56.495Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88", size = 35697297, upload-time = "2025-10-28T17:34:04.722Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/e1/6496dadbc80d8d896ff72511ecfe2316b50313bfc3ebf07a3f580f08bd8c/scipy-1.16.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234", size = 36021756, upload-time = "2025-10-28T17:34:13.482Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fe/bd/a8c7799e0136b987bda3e1b23d155bcb31aec68a4a472554df5f0937eef7/scipy-1.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d", size = 38696566, upload-time = "2025-10-28T17:34:22.384Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304", size = 38529877, upload-time = "2025-10-28T17:35:51.076Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/14/9d9fbcaa1260a94f4bb5b64ba9213ceb5d03cd88841fe9fd1ffd47a45b73/scipy-1.16.3-cp313-cp313-win_arm64.whl", hash = "sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2", size = 25455366, upload-time = "2025-10-28T17:35:59.014Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/a3/9ec205bd49f42d45d77f1730dbad9ccf146244c1647605cf834b3a8c4f36/scipy-1.16.3-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b", size = 37027931, upload-time = "2025-10-28T17:34:31.451Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/06/ca9fd1f3a4589cbd825b1447e5db3a8ebb969c1eaf22c8579bd286f51b6d/scipy-1.16.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079", size = 29400081, upload-time = "2025-10-28T17:34:39.087Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/56/933e68210d92657d93fb0e381683bc0e53a965048d7358ff5fbf9e6a1b17/scipy-1.16.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a", size = 21391244, upload-time = "2025-10-28T17:34:45.234Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/7e/779845db03dc1418e215726329674b40576879b91814568757ff0014ad65/scipy-1.16.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119", size = 23929753, upload-time = "2025-10-28T17:34:51.793Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4c/4b/f756cf8161d5365dcdef9e5f460ab226c068211030a175d2fc7f3f41ca64/scipy-1.16.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c", size = 33496912, upload-time = "2025-10-28T17:34:59.8Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/b5/222b1e49a58668f23839ca1542a6322bb095ab8d6590d4f71723869a6c2c/scipy-1.16.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e", size = 35802371, upload-time = "2025-10-28T17:35:08.173Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/8d/5964ef68bb31829bde27611f8c9deeac13764589fe74a75390242b64ca44/scipy-1.16.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135", size = 36190477, upload-time = "2025-10-28T17:35:16.7Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/f2/b31d75cb9b5fa4dd39a0a931ee9b33e7f6f36f23be5ef560bf72e0f92f32/scipy-1.16.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6", size = 38796678, upload-time = "2025-10-28T17:35:26.354Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/1e/b3723d8ff64ab548c38d87055483714fefe6ee20e0189b62352b5e015bb1/scipy-1.16.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc", size = 38640178, upload-time = "2025-10-28T17:35:35.304Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8e/f3/d854ff38789aca9b0cc23008d607ced9de4f7ab14fa1ca4329f86b3758ca/scipy-1.16.3-cp313-cp313t-win_arm64.whl", hash = "sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a", size = 25803246, upload-time = "2025-10-28T17:35:42.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/f6/99b10fd70f2d864c1e29a28bbcaa0c6340f9d8518396542d9ea3b4aaae15/scipy-1.16.3-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6", size = 36606469, upload-time = "2025-10-28T17:36:08.741Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/74/043b54f2319f48ea940dd025779fa28ee360e6b95acb7cd188fad4391c6b/scipy-1.16.3-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657", size = 28872043, upload-time = "2025-10-28T17:36:16.599Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/e1/24b7e50cc1c4ee6ffbcb1f27fe9f4c8b40e7911675f6d2d20955f41c6348/scipy-1.16.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26", size = 20862952, upload-time = "2025-10-28T17:36:22.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/3a/3e8c01a4d742b730df368e063787c6808597ccb38636ed821d10b39ca51b/scipy-1.16.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc", size = 23508512, upload-time = "2025-10-28T17:36:29.731Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/60/c45a12b98ad591536bfe5330cb3cfe1850d7570259303563b1721564d458/scipy-1.16.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22", size = 33413639, upload-time = "2025-10-28T17:36:37.982Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/bc/35957d88645476307e4839712642896689df442f3e53b0fa016ecf8a3357/scipy-1.16.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc", size = 35704729, upload-time = "2025-10-28T17:36:46.547Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/15/89105e659041b1ca11c386e9995aefacd513a78493656e57789f9d9eab61/scipy-1.16.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0", size = 36086251, upload-time = "2025-10-28T17:36:55.161Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/87/c0ea673ac9c6cc50b3da2196d860273bc7389aa69b64efa8493bdd25b093/scipy-1.16.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800", size = 38716681, upload-time = "2025-10-28T17:37:04.1Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/06/837893227b043fb9b0d13e4bd7586982d8136cb249ffb3492930dab905b8/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d", size = 39358423, upload-time = "2025-10-28T17:38:20.005Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/03/28bce0355e4d34a7c034727505a02d19548549e190bedd13a721e35380b7/scipy-1.16.3-cp314-cp314-win_arm64.whl", hash = "sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f", size = 26135027, upload-time = "2025-10-28T17:38:24.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/6f/69f1e2b682efe9de8fe9f91040f0cd32f13cfccba690512ba4c582b0bc29/scipy-1.16.3-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c", size = 37028379, upload-time = "2025-10-28T17:37:14.061Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/2d/e826f31624a5ebbab1cd93d30fd74349914753076ed0593e1d56a98c4fb4/scipy-1.16.3-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40", size = 29400052, upload-time = "2025-10-28T17:37:21.709Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/69/27/d24feb80155f41fd1f156bf144e7e049b4e2b9dd06261a242905e3bc7a03/scipy-1.16.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d", size = 21391183, upload-time = "2025-10-28T17:37:29.559Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/d3/1b229e433074c5738a24277eca520a2319aac7465eea7310ea6ae0e98ae2/scipy-1.16.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa", size = 23930174, upload-time = "2025-10-28T17:37:36.306Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/9d/d9e148b0ec680c0f042581a2be79a28a7ab66c0c4946697f9e7553ead337/scipy-1.16.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8", size = 33497852, upload-time = "2025-10-28T17:37:42.228Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/22/4e5f7561e4f98b7bea63cf3fd7934bff1e3182e9f1626b089a679914d5c8/scipy-1.16.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353", size = 35798595, upload-time = "2025-10-28T17:37:48.102Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/42/6644d714c179429fc7196857866f219fef25238319b650bb32dde7bf7a48/scipy-1.16.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146", size = 36186269, upload-time = "2025-10-28T17:37:53.72Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/70/64b4d7ca92f9cf2e6fc6aaa2eecf80bb9b6b985043a9583f32f8177ea122/scipy-1.16.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d", size = 38802779, upload-time = "2025-10-28T17:37:59.393Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/82/8d0e39f62764cce5ffd5284131e109f07cf8955aef9ab8ed4e3aa5e30539/scipy-1.16.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7", size = 39471128, upload-time = "2025-10-28T17:38:05.259Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
|
]
|
||||||
Loading…
x
Reference in New Issue
Block a user