change: grid and units improvements

This commit is contained in:
Benoît Sierro
2023-09-05 09:05:39 +02:00
parent a599f55cdb
commit ff7e78b1b5
7 changed files with 66 additions and 30 deletions

View File

@@ -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" }]

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)))

View File

@@ -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:

View File

@@ -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

View File

@@ -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)