change: grid and units improvements
This commit is contained in:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "scgenerator"
|
name = "scgenerator"
|
||||||
version = "0.3.9"
|
version = "0.3.10"
|
||||||
description = "Simulate nonlinear pulse propagation in optical fibers"
|
description = "Simulate nonlinear pulse propagation in optical fibers"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [{ name = "Benoit Sierro", email = "benoit.sierro@iap.unibe.ch" }]
|
authors = [{ name = "Benoit Sierro", email = "benoit.sierro@iap.unibe.ch" }]
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class TimedMessage:
|
|||||||
|
|
||||||
def data_file(path: str) -> Path:
|
def data_file(path: str) -> Path:
|
||||||
"""returns a `Path` object pointing to the desired data file included in `scgenerator`"""
|
"""returns a `Path` object pointing to the desired data file included in `scgenerator`"""
|
||||||
return importlib.resources.path("scgenerator", "data") / path
|
return importlib.resources.files("scgenerator") / "data" / path
|
||||||
|
|
||||||
|
|
||||||
class CustomEncoder(json.JSONEncoder):
|
class CustomEncoder(json.JSONEncoder):
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ def to_dB(arr: np.ndarray, ref=None, axis=None) -> np.ndarray:
|
|||||||
ref = np.max(arr)
|
ref = np.max(arr)
|
||||||
m = arr / ref
|
m = arr / ref
|
||||||
above_0 = m > 0
|
above_0 = m > 0
|
||||||
m = 10 * np.log10(m, out=np.zeros_like(m) - 10 * np.log10(m[above_0].min()), where=above_0)
|
m = 10 * np.log10(m, out=np.ones_like(m) * (10 * np.log10(m[above_0].min())), where=above_0)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
@@ -229,6 +229,38 @@ def tspace(time_window: float | None = None, t_num: float | None = None, dt: flo
|
|||||||
raise TypeError("not enough parameter to determine time vector")
|
raise TypeError("not enough parameter to determine time vector")
|
||||||
|
|
||||||
|
|
||||||
|
def irfftfreq(freq: np.ndarray, retstep: bool = False):
|
||||||
|
"""
|
||||||
|
Given an array of positive only frequency, this returns the corresponding time array centered
|
||||||
|
around 0 that will be aligned with the `numpy.fft.irfft` of a spectrum aligned with `freq`.
|
||||||
|
if `retstep` is True, the sample spacing is returned as well
|
||||||
|
"""
|
||||||
|
df = freq[1] - freq[0]
|
||||||
|
nt = (len(freq) - 1) * 2
|
||||||
|
period = 1 / df
|
||||||
|
dt = period / nt
|
||||||
|
|
||||||
|
t = np.linspace(-(period - dt) / 2, (period - dt) / 2, nt)
|
||||||
|
if retstep:
|
||||||
|
return t, dt
|
||||||
|
else:
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def iwspace(w: np.ndarray, retstep: bool = False):
|
||||||
|
"""invserse of wspace: recovers the (symmetric) time array corresponsding to `w`"""
|
||||||
|
df = (w[1] - w[0]) * 0.5 / np.pi
|
||||||
|
print(df)
|
||||||
|
nt = len(w)
|
||||||
|
period = 1 / df
|
||||||
|
dt = period / nt
|
||||||
|
t = np.linspace(-(period - dt) / 2, (period - dt) / 2, nt)
|
||||||
|
if retstep:
|
||||||
|
return t, dt
|
||||||
|
else:
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
def dt_from_min_wl(wl_min: float, wavelength: float) -> float:
|
def dt_from_min_wl(wl_min: float, wavelength: float) -> float:
|
||||||
return 0.5 * 1 / c * 1 / (1 / wl_min - 1 / wavelength)
|
return 0.5 * 1 / c * 1 / (1 / wl_min - 1 / wavelength)
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ class NoiseMeasurement:
|
|||||||
freq, spec = self.sample_spectrum(nt, dt)
|
freq, spec = self.sample_spectrum(nt, dt)
|
||||||
if phase is None:
|
if phase is None:
|
||||||
phase = 2 * np.pi * np.random.rand(len(freq))
|
phase = 2 * np.pi * np.random.rand(len(freq))
|
||||||
time, dt = irfftfreq(freq, True)
|
time, dt = math.irfftfreq(freq, True)
|
||||||
|
|
||||||
amp = np.sqrt(spec) * np.exp(1j * phase)
|
amp = np.sqrt(spec) * np.exp(1j * phase)
|
||||||
signal = np.fft.irfft(amp) * np.sqrt(0.5 * nt / dt)
|
signal = np.fft.irfft(amp) * np.sqrt(0.5 * nt / dt)
|
||||||
@@ -163,24 +163,6 @@ class NoiseMeasurement:
|
|||||||
return integrated_noise(self.freq, self.psd)
|
return integrated_noise(self.freq, self.psd)
|
||||||
|
|
||||||
|
|
||||||
def irfftfreq(freq: np.ndarray, retstep: bool = False):
|
|
||||||
"""
|
|
||||||
Given an array of positive only frequency, this returns the corresponding time array centered
|
|
||||||
around 0 that will be aligned with the `numpy.fft.irfft` of a spectrum aligned with `freq`.
|
|
||||||
if `retstep` is True, the sample spacing is returned as well
|
|
||||||
"""
|
|
||||||
df = freq[1] - freq[0]
|
|
||||||
nt = (len(freq) - 1) * 2
|
|
||||||
period = 1 / df
|
|
||||||
dt = period / nt
|
|
||||||
|
|
||||||
t = np.linspace(-(period - dt) / 2, (period - dt) / 2, nt)
|
|
||||||
if retstep:
|
|
||||||
return t, dt
|
|
||||||
else:
|
|
||||||
return t
|
|
||||||
|
|
||||||
|
|
||||||
def log_power(x):
|
def log_power(x):
|
||||||
return 10 * np.log10(np.abs(np.where(x == 0, 1e-7, x)))
|
return 10 * np.log10(np.abs(np.where(x == 0, 1e-7, x)))
|
||||||
|
|
||||||
|
|||||||
@@ -28,15 +28,31 @@ class Spectrum(np.ndarray):
|
|||||||
l: np.ndarray
|
l: np.ndarray
|
||||||
ifft: Callable[[np.ndarray], np.ndarray]
|
ifft: Callable[[np.ndarray], np.ndarray]
|
||||||
|
|
||||||
def __new__(cls, input_array, params: Parameters):
|
@classmethod
|
||||||
|
def from_params(cls, array, params: Parameters):
|
||||||
|
w = params.compute("w")
|
||||||
|
t = params.compute("t")
|
||||||
|
ifft = params.compute("ifft")
|
||||||
|
return cls(array, w, t, ifft)
|
||||||
|
|
||||||
|
def __new__(
|
||||||
|
cls,
|
||||||
|
input_array,
|
||||||
|
w: np.ndarray,
|
||||||
|
t: np.ndarray | None = None,
|
||||||
|
ifft: Callable[[np.ndarray], np.ndarray] = np.fft.ifft,
|
||||||
|
):
|
||||||
# Input array is an already formed ndarray instance
|
# Input array is an already formed ndarray instance
|
||||||
# We first cast to be our class type
|
# We first cast to be our class type
|
||||||
obj = np.asarray(input_array).view(cls)
|
obj = np.asarray(input_array).view(cls)
|
||||||
# add the new attribute to the created instance
|
# add the new attribute to the created instance
|
||||||
obj.w = params.compute("w")
|
obj.w = w
|
||||||
obj.t = params.compute("t")
|
if t is not None:
|
||||||
obj.l = params.compute("l")
|
obj.t = t
|
||||||
obj.ifft = params.compute("ifft")
|
else:
|
||||||
|
obj.t = math.iwspace(obj.w)
|
||||||
|
obj.ifft = ifft
|
||||||
|
obj.l = 2 * np.pi * units.c / obj.w
|
||||||
|
|
||||||
if not (len(obj.w) == len(obj.t) == len(obj.l) == obj.shape[-1]):
|
if not (len(obj.w) == len(obj.t) == len(obj.l) == obj.shape[-1]):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
@@ -187,7 +203,7 @@ class Propagation(Generic[ParamsOrNone]):
|
|||||||
key = len(self) + key
|
key = len(self) + key
|
||||||
array = self.io.load_spectrum(key)
|
array = self.io.load_spectrum(key)
|
||||||
if self.parameters is not None:
|
if self.parameters is not None:
|
||||||
return Spectrum(array, self.parameters)
|
return Spectrum.from_params(array, self.parameters)
|
||||||
else:
|
else:
|
||||||
return array
|
return array
|
||||||
|
|
||||||
@@ -207,7 +223,7 @@ class Propagation(Generic[ParamsOrNone]):
|
|||||||
def _load_slice(self, key: slice) -> Spectrum:
|
def _load_slice(self, key: slice) -> Spectrum:
|
||||||
_iter = range(len(self))[key]
|
_iter = range(len(self))[key]
|
||||||
if self.parameters is not None:
|
if self.parameters is not None:
|
||||||
out = Spectrum(
|
out = Spectrum.from_params(
|
||||||
np.zeros((len(_iter), self.parameters.t_num), dtype=complex), self.parameters
|
np.zeros((len(_iter), self.parameters.t_num), dtype=complex), self.parameters
|
||||||
)
|
)
|
||||||
for i in _iter:
|
for i in _iter:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ scgenerator module but some function may be used in any python program
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import datetime
|
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|||||||
@@ -20,3 +20,10 @@ def test_wl_dispersion():
|
|||||||
wl = sc.units.m.inv(w + sc.units.nm(1546))
|
wl = sc.units.m.inv(w + sc.units.nm(1546))
|
||||||
wl_disp, ind_disp = sc.fiber.lambda_for_envelope_dispersion(wl, (950e-9, 4000e-9))
|
wl_disp, ind_disp = sc.fiber.lambda_for_envelope_dispersion(wl, (950e-9, 4000e-9))
|
||||||
assert all(np.diff(wl_disp) > 0)
|
assert all(np.diff(wl_disp) > 0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_iwspace():
|
||||||
|
t = sc.tspace(dt=15.6, t_num=512)
|
||||||
|
assert sc.math.iwspace(sc.wspace(t)) == pytest.approx(t)
|
||||||
|
assert sc.math.iwspace(sc.wspace(t) + 4564568456.4) != pytest.approx(t)
|
||||||
|
assert sc.math.iwspace(sc.wspace(t) + 4564568456.4) == pytest.approx(t, rel=1e-3)
|
||||||
|
|||||||
Reference in New Issue
Block a user