diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20a8810 --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# VSCode +.vscode +pypho diff --git a/src/single-core-regen/_path_fix.py b/src/single-core-regen/_path_fix.py new file mode 100644 index 0000000..72e5669 --- /dev/null +++ b/src/single-core-regen/_path_fix.py @@ -0,0 +1,9 @@ +import sys +from pathlib import Path + +# hack to add the parent directory to the path -> pypho doesn't have to be installed as package +parent_dir = Path(__file__).parent +while not (parent_dir / "pypho").exists() and parent_dir != Path("/"): + parent_dir = parent_dir.parent +print(f"Adding '{parent_dir}' to 'sys.path' to enable import of '{parent_dir / 'pypho'}'") +sys.path.append(str(parent_dir)) diff --git a/src/single-core-regen/generate_signal.py b/src/single-core-regen/generate_signal.py new file mode 100644 index 0000000..2028f53 --- /dev/null +++ b/src/single-core-regen/generate_signal.py @@ -0,0 +1,216 @@ +import configparser +from datetime import datetime +import hashlib +from pathlib import Path +from matplotlib import pyplot as plt +import numpy as np + +from rich import inspect + +import _path_fix # noqa: F401 +import pypho +import io +# import inspect + +default_config = f""" +[glova] +sps = 256 +nos = 256 +f0 = 193414489032258.06 +symbolrate = 10e9 +wisdom_dir = "{str((Path.home()/".pypho"))}" +flags = "FFTW_PATIENT" +nthreads = 32 + +[fiber] +length = 80000 +gamma = 1.14 +alpha = 0.2 +D = 17 +S = 0 +birefsteps = 1 +birefseed = 0xC0FFEE + +[signal] +modulation = "pam" +mod_order = 4 +seed = 0xC0FFEE +pulse_shape = "gauss_rz" +fwhm = 0.33 + +[script] +data_dir = "{str((Path.home()/".pypho"/"data"))}" +""" + + + +def get_config(config_file=None): + """ + DANGER! The function uses eval() to parse the config file. Do not use this function with untrusted input. + """ + if config_file is None: + config_file = Path(__file__).parent / "signal_generation.ini" + if not config_file.exists(): + with open(config_file, "w") as f: + f.write(default_config) + config = configparser.ConfigParser() + config.read(config_file) + + conf = {} + for section in config.sections(): + # print(f"[{section}]") + conf[section] = {} + for key in config[section]: + # print(f"{key} = {config[section][key]}") + conf[section][key] = eval(config[section][key]) + # if isinstance(conf[section][key], str): + # conf[section][key] = config[section][key].strip('"') + return conf + +def initialize_fiber_and_data(config, input_data_override=None): + py_glova = pypho.setup( + nos = config['glova']['nos'], + sps = config['glova']['sps'], + f0 = config['glova']['f0'], + symbolrate=config['glova']['symbolrate'], + wisdom_dir = config['glova']['wisdom_dir'], + flags = config['glova']['flags'], + nthreads = config['glova']['nthreads'], + ) + + c_glova = pypho.cfiber.GlovaWrapper.from_setup(py_glova) + c_data = pypho.cfiber.DataWrapper(py_glova.sps * py_glova.nos) + + if input_data_override is not None: + c_data.E_in = input_data_override + else: + symbolsrc = pypho.symbols(py_glova, py_glova.nos, pattern='ones', seed=config['signal']['seed']) + esigsrc = pypho.signalsrc(py_glova, pulseshape=config['signal']['pulse_shape'], fwhm=config['signal']['fwhm']) + sig = pypho.lasmod(py_glova, power=0, Df=0, theta=np.pi/4) + modulator = pypho.arbmod(py_glova) + + symbols_x = symbolsrc(pattern='random', p1=config['signal']['mod_order']) + symbols_y = symbolsrc(pattern='random', p1=config['signal']['mod_order']) + + ones = symbolsrc(pattern='ones') + esig = esigsrc(bitsequence=ones)*np.sqrt(2) + + source_signal = sig(esig) + + constpts_x = [np.linspace(1/config['signal']['mod_order'], 1, config['signal']['mod_order'], endpoint=True, dtype=np.complex128)] + constpts_y = [np.linspace(1/config['signal']['mod_order'], 1, config['signal']['mod_order'], endpoint=True, dtype=np.complex128)] + + source_signal = modulator(E=source_signal, symbols = (symbols_x, symbols_y), constpoints= (constpts_x, constpts_y)) + + c_data.E_in = source_signal[0]['E'] + + py_fiber = pypho.fiber( + glova = py_glova, + l=config['fiber']['length'], + alpha=pypho.functions.dB_to_Neper(config['fiber']['alpha'])/1000, + gamma=config['fiber']['gamma'], + D=config['fiber']['d'], + S=config['fiber']['s'], + ) + py_fiber.birefarray = pypho.birefringence_segment.create_pmd_fibre(py_fiber.l, py_fiber.l/config['fiber']['birefsteps'], 0, config['fiber']['birefseed']) + c_params = pypho.cfiber.ParamsWrapper.from_fiber(py_fiber) + c_fiber = pypho.cfiber.FiberWrapper(c_data, c_params, c_glova) + + return c_fiber, c_data + + +def save_data(data, config): + data_dir = Path(config['script']['data_dir']) + save_dir = data_dir + save_dir.mkdir(parents=True, exist_ok=True) + save_data = np.column_stack([data.E_in[0], data.E_in[1], data.E_out[0], data.E_out[1]]) + timestamp = datetime.now() + config_content = '\n'.join(( + f"; Generated by {str(Path(__file__).name)} @ {timestamp.strftime("%Y-%m-%d %H:%M:%S")}", + "[glova]", + f"sps = {config['glova']['sps']}", + f"nos = {config['glova']['nos']}", + f"f0 = {config['glova']['f0']}", + f"symbolrate = {config['glova']['symbolrate']}", + f'wisdom_dir = "{config['glova']['wisdom_dir']}"', + f'flags = "{config["glova"]["flags"]}"', + f"nthreads = {config['glova']['nthreads']}", + "", + "[fiber]", + f"length = {config['fiber']['length']}", + f"gamma = {config['fiber']['gamma']}", + f"alpha = {config['fiber']['alpha']}", + f"D = {config['fiber']['d']}", + f"S = {config['fiber']['s']}", + f"birefsteps = {config['fiber']['birefsteps']}", + f"birefseed = {config['fiber']['birefseed']}", + "", + "[signal]", + f'modulation = "{config['signal']['modulation']}"', + f"mod_order = {config['signal']['mod_order']}", + f"seed = {config['signal']['seed']}", + f'pulse_shape = "{config["signal"]["pulse_shape"]}"', + f"fwhm = {config['signal']['fwhm']}", + "", + "[script]", + f'data_dir = "{config["script"]["data_dir"]}"', + )) + config_hash = hashlib.md5(config_content.encode()).hexdigest() + save_file = f"npys/{config_hash}.npy" + config_content += f'\n\n[DATA]\nfile = "{str(save_file)}\n"' + + filename_components = ( + timestamp.strftime("%Y%m%d-%H%M%S"), + config['glova']['sps'], + config['glova']['nos'], + config['fiber']['length'], + config['fiber']['gamma'], + config['fiber']['alpha'], + config['fiber']['d'], + config['fiber']['s'], + f"{config['signal']['modulation'].upper()}{config['signal']['mod_order']}", + ) + + lookup_file = "-".join(map(str, filename_components)) + ".ini" + with open(data_dir / lookup_file, "w") as f: + f.write(config_content) + + np.save(save_dir / save_file, save_data) + + print("Saved config to", data_dir / lookup_file) + print("Saved data to", save_dir / f"{config_hash}.npy") + + + +if __name__ == "__main__": + config = get_config() + + # loop over lengths + # lengths = [1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000] + lengths = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000] + lengths = sorted(lengths) + input_override = None + for lind, length in enumerate(lengths): + print(f"\nGenerating data for fiber length {length}") + # if lind > 0: + # # set the length to the difference between the current and previous length -> incremental + # length = lengths[lind] - lengths[lind-1] + # print(f"\nGenerating data for fiber length {lengths[lind]}m -> {length}m") + config['fiber']['length'] = length + # set the input data to the output data of the previous run + cfiber, cdata = initialize_fiber_and_data(config, input_data_override=input_override) + cfiber() + # input_override = cdata.E_out + save_data(cdata, config) + # cfiber, cdata = initialize_fiber_and_data(config) + # cfiber() + # save_data(cdata, config) + + # fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) + # xax = np.linspace(0, cfiber.glova.nos, cfiber.glova.nos*cfiber.glova.sps)-0.5 + # axs[0,0].plot(xax,np.abs(cdata.E_in[0])) + # axs[1,0].plot(xax,np.abs(cdata.E_in[1])) + # axs[0,1].plot(xax,np.abs(cdata.E_out[0])) + # axs[1,1].plot(xax,np.abs(cdata.E_out[1])) + + # plt.show() diff --git a/src/single-core-regen/signal_generation.ini b/src/single-core-regen/signal_generation.ini new file mode 100644 index 0000000..9d2ae16 --- /dev/null +++ b/src/single-core-regen/signal_generation.ini @@ -0,0 +1,31 @@ +[glova] +; sps = 256 +; nos = 2**17 +sps = 128 +nos = 2**14 +f0 = 193414489032258.06 +; -> delta0 = 1550.0 nm +symbolrate = 10e9 +wisdom_dir = "/home/suppl/.pypho" +flags = "FFTW_PATIENT" +nthreads = 32 + +[fiber] +length = 1000 +gamma = 1.14 +alpha = 0.2 +D = 17 +; 1700 ps/nm/km @ 1 km length is equivalent to 17 ps/nm/km @ 100 km length but the simulation is way faster +S = 0 +birefsteps = 0 +; birefseed = 0xC0FFEE + +[signal] +modulation = "pam" +mod_order = 4 +; seed = 0xC0FFEE +pulse_shape = "gauss_rz" +fwhm = 0.33 + +[script] +data_dir = "/home/suppl/.pypho/data" \ No newline at end of file