PyOptik 2.0.7__tar.gz → 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. {pyoptik-2.0.7 → pyoptik-3.0.0}/.github/workflows/deploy_anaconda.yml +2 -2
  2. pyoptik-3.0.0/.github/workflows/tests.yml +29 -0
  3. {pyoptik-2.0.7 → pyoptik-3.0.0}/.gitignore +1 -0
  4. pyoptik-3.0.0/Makefile +60 -0
  5. pyoptik-3.0.0/PKG-INFO +420 -0
  6. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/__init__.py +5 -2
  7. pyoptik-3.0.0/PyOptik/__main__.py +124 -0
  8. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/_version.py +3 -3
  9. pyoptik-3.0.0/PyOptik/catalog.py +592 -0
  10. pyoptik-3.0.0/PyOptik/directories.py +81 -0
  11. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/material/base_class.py +76 -9
  12. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/material/sellmeier_class.py +49 -16
  13. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/material/tabulated_class.py +46 -13
  14. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/utils.py +28 -19
  15. pyoptik-3.0.0/PyOptik.egg-info/PKG-INFO +420 -0
  16. pyoptik-3.0.0/PyOptik.egg-info/SOURCES.txt +67 -0
  17. pyoptik-3.0.0/PyOptik.egg-info/entry_points.txt +2 -0
  18. pyoptik-3.0.0/PyOptik.egg-info/scm_file_list.json +63 -0
  19. pyoptik-3.0.0/PyOptik.egg-info/scm_version.json +8 -0
  20. pyoptik-3.0.0/README.rst +360 -0
  21. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/group_properties/plot_group_properties.py +7 -6
  22. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/sellmeier/plot_bk7.py +3 -2
  23. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/sellmeier/plot_compare_glasses.py +7 -6
  24. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/sellmeier/plot_silica.py +3 -2
  25. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/sellmeier/plot_water.py +3 -2
  26. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/tabulated/plot_polyethylene.py +5 -4
  27. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/tabulated/plot_silicon_nk.py +5 -4
  28. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/tabulated/plot_silver.py +4 -3
  29. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/code.rst +28 -15
  30. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/conf.py +1 -9
  31. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/examples.rst +0 -1
  32. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/index.rst +2 -2
  33. pyoptik-3.0.0/docs/source/references.rst +25 -0
  34. {pyoptik-2.0.7 → pyoptik-3.0.0}/meta.yaml +9 -4
  35. {pyoptik-2.0.7 → pyoptik-3.0.0}/pyproject.toml +3 -1
  36. {pyoptik-2.0.7 → pyoptik-3.0.0}/pytest.ini +4 -0
  37. pyoptik-3.0.0/tests/conftest.py +23 -0
  38. pyoptik-3.0.0/tests/test_catalog.py +140 -0
  39. pyoptik-3.0.0/tests/test_docstrings.py +16 -0
  40. pyoptik-3.0.0/tests/test_main_cli.py +88 -0
  41. pyoptik-3.0.0/tests/test_material_models.py +64 -0
  42. pyoptik-3.0.0/tests/test_validation_and_logging.py +16 -0
  43. pyoptik-2.0.7/PKG-INFO +0 -427
  44. pyoptik-2.0.7/PyOptik/__main__.py +0 -37
  45. pyoptik-2.0.7/PyOptik/data/sellmeier/BAF10.yml +0 -59
  46. pyoptik-2.0.7/PyOptik/data/sellmeier/BAK1.yml +0 -63
  47. pyoptik-2.0.7/PyOptik/data/sellmeier/BK7.yml +0 -57
  48. pyoptik-2.0.7/PyOptik/data/sellmeier/SF5.yml +0 -57
  49. pyoptik-2.0.7/PyOptik/data/sellmeier/ZBLAN.yml +0 -14
  50. pyoptik-2.0.7/PyOptik/data/sellmeier/acetone.yml +0 -16
  51. pyoptik-2.0.7/PyOptik/data/sellmeier/air.yml +0 -18
  52. pyoptik-2.0.7/PyOptik/data/sellmeier/argon.yml +0 -21
  53. pyoptik-2.0.7/PyOptik/data/sellmeier/crown.yml +0 -61
  54. pyoptik-2.0.7/PyOptik/data/sellmeier/flint.yml +0 -60
  55. pyoptik-2.0.7/PyOptik/data/sellmeier/fused_silica.yml +0 -20
  56. pyoptik-2.0.7/PyOptik/data/sellmeier/germanium.yml +0 -18
  57. pyoptik-2.0.7/PyOptik/data/sellmeier/lithium_niobate.yml +0 -17
  58. pyoptik-2.0.7/PyOptik/data/sellmeier/polystyren.yml +0 -17
  59. pyoptik-2.0.7/PyOptik/data/sellmeier/soda_lime_glass.yml +0 -122
  60. pyoptik-2.0.7/PyOptik/data/sellmeier/water.yml +0 -16
  61. pyoptik-2.0.7/PyOptik/data/tabulated/aluminium.yml +0 -219
  62. pyoptik-2.0.7/PyOptik/data/tabulated/calcium.yml +0 -74
  63. pyoptik-2.0.7/PyOptik/data/tabulated/copper.yml +0 -62
  64. pyoptik-2.0.7/PyOptik/data/tabulated/gold.yml +0 -62
  65. pyoptik-2.0.7/PyOptik/data/tabulated/iron.yml +0 -74
  66. pyoptik-2.0.7/PyOptik/data/tabulated/nickel.yml +0 -62
  67. pyoptik-2.0.7/PyOptik/data/tabulated/polyetylene.yml +0 -34
  68. pyoptik-2.0.7/PyOptik/data/tabulated/silicon.yml +0 -59
  69. pyoptik-2.0.7/PyOptik/data/tabulated/silver.yml +0 -62
  70. pyoptik-2.0.7/PyOptik/data/tabulated/sodium.yml +0 -34
  71. pyoptik-2.0.7/PyOptik/data/tabulated/zinc.yml +0 -163
  72. pyoptik-2.0.7/PyOptik/directories.py +0 -53
  73. pyoptik-2.0.7/PyOptik/libraries/__init__.py +0 -0
  74. pyoptik-2.0.7/PyOptik/libraries/classics.yml +0 -30
  75. pyoptik-2.0.7/PyOptik/libraries/dielectrics.yml +0 -22
  76. pyoptik-2.0.7/PyOptik/libraries/metals.yml +0 -20
  77. pyoptik-2.0.7/PyOptik/libraries/minimal.yml +0 -9
  78. pyoptik-2.0.7/PyOptik/libraries/organics.yml +0 -11
  79. pyoptik-2.0.7/PyOptik/libraries/others.yml +0 -4
  80. pyoptik-2.0.7/PyOptik/libraries/polymers.yml +0 -9
  81. pyoptik-2.0.7/PyOptik/libraries/repertoire.yml +0 -59
  82. pyoptik-2.0.7/PyOptik/material_bank.py +0 -529
  83. pyoptik-2.0.7/PyOptik.egg-info/PKG-INFO +0 -427
  84. pyoptik-2.0.7/PyOptik.egg-info/SOURCES.txt +0 -108
  85. pyoptik-2.0.7/README.rst +0 -367
  86. pyoptik-2.0.7/development/dev_0.py +0 -9
  87. pyoptik-2.0.7/docs/examples/utils/README.rst +0 -6
  88. pyoptik-2.0.7/docs/examples/utils/__build_library.py +0 -30
  89. pyoptik-2.0.7/docs/examples/utils/__download_yml_file.py +0 -28
  90. pyoptik-2.0.7/docs/examples/utils/create_sellmeier_file.py +0 -29
  91. pyoptik-2.0.7/docs/examples/utils/create_tabulated_file.py +0 -37
  92. pyoptik-2.0.7/docs/examples/utils/search_materials.py +0 -23
  93. pyoptik-2.0.7/docs/source/references.rst +0 -4
  94. pyoptik-2.0.7/docs/source/sg_execution_times.rst +0 -73
  95. pyoptik-2.0.7/tests/__init__.py +0 -0
  96. pyoptik-2.0.7/tests/test_group_properties.py +0 -42
  97. pyoptik-2.0.7/tests/test_main_cli.py +0 -24
  98. pyoptik-2.0.7/tests/test_material_bank_extra.py +0 -47
  99. pyoptik-2.0.7/tests/test_sellmeier.py +0 -183
  100. pyoptik-2.0.7/tests/test_tabulated.py +0 -128
  101. pyoptik-2.0.7/tests/test_usual_materials.py +0 -51
  102. pyoptik-2.0.7/tests/test_utils.py +0 -165
  103. {pyoptik-2.0.7 → pyoptik-3.0.0}/.coveragerc +0 -0
  104. {pyoptik-2.0.7 → pyoptik-3.0.0}/.flake8 +0 -0
  105. {pyoptik-2.0.7 → pyoptik-3.0.0}/.github/dependabot.yml +0 -0
  106. {pyoptik-2.0.7 → pyoptik-3.0.0}/.github/workflows/deploy_PyPi.yml +0 -0
  107. {pyoptik-2.0.7 → pyoptik-3.0.0}/.github/workflows/deploy_coverage.yml +0 -0
  108. {pyoptik-2.0.7 → pyoptik-3.0.0}/.github/workflows/deploy_documentation.yml +0 -0
  109. {pyoptik-2.0.7 → pyoptik-3.0.0}/.pre-commit-config.yaml +0 -0
  110. {pyoptik-2.0.7 → pyoptik-3.0.0}/LICENSE +0 -0
  111. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/material/__init__.py +0 -0
  112. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik/material_type.py +0 -0
  113. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik.egg-info/dependency_links.txt +0 -0
  114. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik.egg-info/requires.txt +0 -0
  115. {pyoptik-2.0.7 → pyoptik-3.0.0}/PyOptik.egg-info/top_level.txt +0 -0
  116. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/Makefile +0 -0
  117. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/README.rst +0 -0
  118. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/group_properties/README.rst +0 -0
  119. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/sellmeier/README.rst +0 -0
  120. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/examples/tabulated/README.rst +0 -0
  121. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/images/example_bk7.png +0 -0
  122. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/images/logo.png +0 -0
  123. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/make.bat +0 -0
  124. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/_static/default.css +0 -0
  125. {pyoptik-2.0.7 → pyoptik-3.0.0}/docs/source/_static/thumbnail.png +0 -0
  126. {pyoptik-2.0.7 → pyoptik-3.0.0}/setup.cfg +0 -0
  127. {pyoptik-2.0.7/PyOptik/data → pyoptik-3.0.0/tests}/__init__.py +0 -0
  128. {pyoptik-2.0.7 → pyoptik-3.0.0}/tests/test_base_material_decorator.py +0 -0
  129. {pyoptik-2.0.7 → pyoptik-3.0.0}/tests/test_utils_extra.py +0 -0
@@ -14,8 +14,8 @@ jobs:
14
14
  linux:
15
15
  uses: MartinPdeS/MPSActions/.github/workflows/publish_compiled_package_to_anaconda.yml@master
16
16
  with:
17
- python_versions: '["3.11", "3.12", "3.13"]'
17
+ python_versions: '["3.13"]'
18
18
  apt_package: ""
19
- os_list: '["ubuntu-latest", "macos-latest", "windows-latest"]'
19
+ os_list: '["ubuntu-latest"]'
20
20
  secrets:
21
21
  ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }}
@@ -0,0 +1,29 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ pull_request:
7
+ branches: ["master"]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ cache: pip
23
+ - name: Install package and test dependencies
24
+ run: python -m pip install --upgrade pip && python -m pip install -e '.[testing]'
25
+ - name: Run offline test suite
26
+ env:
27
+ MPLBACKEND: Agg
28
+ MPLCONFIGDIR: ${{ runner.temp }}/matplotlib
29
+ run: python -m pytest -m 'not network' --cov=PyOptik --cov-report=term-missing
@@ -17,6 +17,7 @@
17
17
  !README.rst
18
18
  !notebook.ipynb
19
19
  !CMakeLists.txt
20
+ !Makefile
20
21
  !pytest.ini
21
22
  !.coveragerc
22
23
 
pyoptik-3.0.0/Makefile ADDED
@@ -0,0 +1,60 @@
1
+ PYTHON ?= python3.11
2
+ BUILD_DIR ?= build
3
+ DIST_DIR ?= dist
4
+ DOCS_DIR ?= docs
5
+ DOCS_BUILD_DIR ?= $(DOCS_DIR)/build
6
+
7
+ .PHONY: help quality test docs build install uninstall quick rebuild editable \
8
+ setup download-all clean
9
+
10
+ help:
11
+ @echo "PyOptik development targets:"
12
+ @echo " make quality Run static checks"
13
+ @echo " make test Run the offline test suite"
14
+ @echo " make docs Build the documentation with warnings as errors"
15
+ @echo " make build Build source and wheel distributions"
16
+ @echo " make install Install the package"
17
+ @echo " make editable Install the package in editable mode"
18
+ @echo " make setup Download the complete material snapshot"
19
+ @echo " make download-all Download the complete upstream catalog"
20
+ @echo " make quick Run quality checks, tests, and build"
21
+ @echo " make rebuild Clean, then run the quick workflow"
22
+ @echo " make clean Remove generated build and test artifacts"
23
+
24
+ quality:
25
+ $(PYTHON) -m flake8 PyOptik tests
26
+
27
+ test:
28
+ MPLBACKEND=Agg MPLCONFIGDIR=$${TMPDIR:-/tmp}/pyoptik-matplotlib \
29
+ $(PYTHON) -m pytest -m 'not network'
30
+
31
+ docs:
32
+ MPLBACKEND=Agg MPLCONFIGDIR=$${TMPDIR:-/tmp}/pyoptik-matplotlib \
33
+ $(PYTHON) -m sphinx -b html -W --keep-going \
34
+ $(DOCS_DIR)/source $(DOCS_BUILD_DIR)/html
35
+
36
+ build:
37
+ $(PYTHON) -m build --outdir $(DIST_DIR)
38
+
39
+ install:
40
+ $(PYTHON) -m pip install .
41
+
42
+ uninstall:
43
+ $(PYTHON) -m pip uninstall -y PyOptik
44
+
45
+ quick: quality test build
46
+
47
+ rebuild: clean quick
48
+
49
+ editable:
50
+ $(PYTHON) -m pip install --no-build-isolation -e .
51
+
52
+ setup:
53
+ $(PYTHON) -m PyOptik setup $(ARGS)
54
+
55
+ download-all:
56
+ $(PYTHON) -m PyOptik download-all $(ARGS)
57
+
58
+ clean:
59
+ rm -rf $(BUILD_DIR) $(DIST_DIR) $(DOCS_BUILD_DIR) \
60
+ .pytest_cache htmlcov .coverage
pyoptik-3.0.0/PKG-INFO ADDED
@@ -0,0 +1,420 @@
1
+ Metadata-Version: 2.4
2
+ Name: PyOptik
3
+ Version: 3.0.0
4
+ Summary: A package for refractive index values.
5
+ Author-email: Martin Poinsinet de Sivry-Houle <martin.poinsinet.de.sivry@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2020 Martin de Sivry
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Keywords: refractive index,optics
29
+ Classifier: Programming Language :: Python
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: Programming Language :: Python :: Implementation :: CPython
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Topic :: Scientific/Engineering :: Physics
34
+ Classifier: Intended Audience :: Science/Research
35
+ Requires-Python: >=3.10
36
+ Description-Content-Type: text/x-rst
37
+ License-File: LICENSE
38
+ Requires-Dist: pint~=0.24
39
+ Requires-Dist: numpy
40
+ Requires-Dist: matplotlib~=3.8
41
+ Requires-Dist: requests~=2.31
42
+ Requires-Dist: pyyaml~=6.0
43
+ Requires-Dist: MPSPlots
44
+ Requires-Dist: tabulate~=0.9
45
+ Requires-Dist: TypedUnit
46
+ Provides-Extra: testing
47
+ Requires-Dist: pytest<9.0,>=7.4; extra == "testing"
48
+ Requires-Dist: pytest-cov<6,>=2; extra == "testing"
49
+ Requires-Dist: pytest-json-report~=1.5; extra == "testing"
50
+ Requires-Dist: coverage~=7.6; extra == "testing"
51
+ Provides-Extra: documentation
52
+ Requires-Dist: numpydoc==1.6.0; extra == "documentation"
53
+ Requires-Dist: sphinx>=5.1.1; extra == "documentation"
54
+ Requires-Dist: sphinx-gallery==0.15.0; extra == "documentation"
55
+ Requires-Dist: sphinx-rtd-theme==2.0.0; extra == "documentation"
56
+ Requires-Dist: pydata-sphinx-theme==0.14.1; extra == "documentation"
57
+ Provides-Extra: dev
58
+ Requires-Dist: flake8==7.1.1; extra == "dev"
59
+ Dynamic: license-file
60
+
61
+ |logo|
62
+
63
+ .. list-table::
64
+ :widths: 35 65
65
+ :header-rows: 1
66
+
67
+ * - Badge
68
+ - Status
69
+ * - Python versions
70
+ - |python|
71
+ * - Documentation
72
+ - |docs|
73
+ * - Continuous integration
74
+ - |ci/cd|
75
+ * - Test coverage
76
+ - |coverage|
77
+ * - PyPI package
78
+ - |PyPi|
79
+ * - PyPI downloads
80
+ - |PyPi_download|
81
+ * - Anaconda package
82
+ - |anaconda|
83
+ * - Anaconda downloads
84
+ - |anaconda_download|
85
+
86
+ PyOptik
87
+ =======
88
+
89
+ **PyOptik** is a Python library for evaluating optical material properties.
90
+ It provides unit-aware refractive-index calculations, dispersion models,
91
+ tabulated optical constants, group-delay properties, plotting helpers, and a
92
+ catalog interface for the hierarchical `RefractiveIndex.INFO
93
+ <https://refractiveindex.info>`_ database.
94
+
95
+ The library is designed for optical design, photonics simulations,
96
+ electromagnetic modeling, and experimental data analysis.
97
+
98
+ Features
99
+ --------
100
+
101
+ * Sellmeier and other dispersion-formula models.
102
+ * Tabulated complex refractive index data, ``n + i k``.
103
+ * Unit-aware wavelength calculations through ``TypedUnit`` and Pint.
104
+ * Group index, group velocity, group delay, and group-delay dispersion.
105
+ * NumPy-compatible scalar and array evaluation.
106
+ * Plotting helpers for dispersion and absorption data.
107
+ * Hierarchical catalog access using upstream ``shelf / book / page`` identity.
108
+ * Explicit upstream page selection using ``shelf/book/page`` identifiers.
109
+ * Downloadable custom and upstream material data with local caching.
110
+
111
+ Installation
112
+ ------------
113
+
114
+ Install the latest release from PyPI:
115
+
116
+ .. code-block:: bash
117
+
118
+ python -m pip install PyOptik
119
+
120
+ The package is also available through Anaconda:
121
+
122
+ .. code-block:: bash
123
+
124
+ conda install -c martinpdes pyoptik
125
+
126
+ Verify the installation with the same interpreter that will run your code:
127
+
128
+ .. code-block:: bash
129
+
130
+ python -c "import PyOptik; print(PyOptik.__version__)"
131
+
132
+ Material data is not bundled with PyOptik. Install the package first, then
133
+ download the upstream snapshot when it is needed:
134
+
135
+ .. code-block:: bash
136
+
137
+ pyoptik setup
138
+
139
+ or from Python:
140
+
141
+ .. code-block:: python
142
+
143
+ from PyOptik import download_snapshot
144
+
145
+ catalog = download_snapshot()
146
+
147
+ First calculation
148
+ -----------------
149
+
150
+ Wavelengths should carry units. This avoids ambiguity between metres,
151
+ micrometres, and nanometres.
152
+
153
+ .. code-block:: python
154
+
155
+ from TypedUnit import ureg
156
+ from PyOptik import MaterialCatalog
157
+
158
+ catalog = MaterialCatalog.from_snapshot()
159
+ bk7 = catalog.get("specs/SCHOTT-optical/P-BK7").load()
160
+ index = bk7.compute_refractive_index(550 * ureg.nanometer)
161
+
162
+ print(index)
163
+
164
+ For backward compatibility, bare numeric wavelengths are interpreted as
165
+ metres. Unit-bearing quantities are recommended for new code.
166
+
167
+ Material models
168
+ ---------------
169
+
170
+ Sellmeier materials evaluate a dispersion formula:
171
+
172
+ .. code-block:: python
173
+
174
+ from TypedUnit import ureg
175
+ from PyOptik import MaterialCatalog
176
+
177
+ catalog = MaterialCatalog.from_snapshot()
178
+ silica = catalog.get("main/SiO2/Malitson").load()
179
+ wavelengths = [800, 1310, 1550] * ureg.nanometer
180
+ index = silica.compute_refractive_index(wavelengths)
181
+
182
+ Tabulated materials interpolate complex optical constants:
183
+
184
+ .. code-block:: python
185
+
186
+ from PyOptik import MaterialCatalog
187
+
188
+ catalog = MaterialCatalog.from_snapshot()
189
+ silicon = catalog.get("main/Si/Aspnes").load()
190
+ index = silicon.compute_refractive_index(1.55 * ureg.micrometer)
191
+
192
+ The real part is the refractive index ``n`` and the imaginary part is the
193
+ extinction coefficient ``k``.
194
+
195
+ Validity ranges
196
+ ~~~~~~~~~~~~~~~
197
+
198
+ Material data is only valid over the wavelength range supplied by its source.
199
+ Out-of-range behavior can be selected explicitly:
200
+
201
+ .. code-block:: python
202
+
203
+ wavelength = 300 * ureg.nanometer
204
+
205
+ # Default: issue a warning and evaluate.
206
+ index = bk7.compute_refractive_index(wavelength, out_of_range="warn")
207
+
208
+ # Fail fast for production calculations.
209
+ index = bk7.compute_refractive_index(wavelength, out_of_range="raise")
210
+
211
+ # Evaluate at the nearest validity boundary.
212
+ index = bk7.compute_refractive_index(wavelength, out_of_range="clip")
213
+
214
+ Group and pulse properties
215
+ --------------------------
216
+
217
+ Every material model provides group-related quantities:
218
+
219
+ .. code-block:: python
220
+
221
+ wavelength = 1550 * ureg.nanometer
222
+
223
+ group_index = silica.compute_group_index(wavelength)
224
+ group_velocity = silica.compute_group_velocity(wavelength)
225
+ group_delay = silica.compute_group_delay(
226
+ wavelength,
227
+ length=10 * ureg.centimeter,
228
+ )
229
+ group_delay_dispersion = silica.compute_group_delay_dispersion(wavelength)
230
+
231
+ These methods accept scalar or array wavelengths and return unit-aware values.
232
+
233
+ Plotting
234
+ --------
235
+
236
+ Material models include simple dispersion plots:
237
+
238
+ .. code-block:: python
239
+
240
+ from PyOptik import MaterialCatalog
241
+
242
+ catalog = MaterialCatalog.from_snapshot()
243
+ catalog.get("specs/SCHOTT-optical/P-BK7").load().plot()
244
+ catalog.get("main/Au/Johnson").load().plot()
245
+
246
+ For non-interactive environments such as CI or servers, select a headless
247
+ Matplotlib backend before importing plotting code:
248
+
249
+ .. code-block:: python
250
+
251
+ import matplotlib
252
+ matplotlib.use("Agg")
253
+
254
+ Hierarchical material catalog
255
+ -----------------------------
256
+
257
+ RefractiveIndex.INFO organizes data by **shelf**, **book**, and **page**.
258
+ PyOptik preserves this identity so that materials from different sources do
259
+ not collide simply because they share a short name.
260
+
261
+ Load the upstream catalog index:
262
+
263
+ .. code-block:: python
264
+
265
+ from PyOptik import MaterialCatalog
266
+
267
+ catalog = MaterialCatalog.from_upstream()
268
+
269
+ print(catalog.shelves())
270
+ print(catalog.books(shelf="specs"))
271
+
272
+ Download a complete source collection, such as an optical-glass book:
273
+
274
+ .. code-block:: python
275
+
276
+ catalog.download(
277
+ shelf="specs",
278
+ book="SCHOTT-optical",
279
+ )
280
+
281
+ Download the complete optical catalog, including tabulated and formula-based
282
+ materials, with resumable progress tracking:
283
+
284
+ .. code-block:: python
285
+
286
+ catalog.download_all()
287
+
288
+ Each page is stored once and recorded in ``manifest.json`` with its source URL,
289
+ local path, status, timestamp, and SHA-256 checksum. Re-running the command
290
+ uses cached files, so interrupted downloads can resume safely.
291
+
292
+ Access a page by its canonical identifier and load its material model:
293
+
294
+ .. code-block:: python
295
+
296
+ page = catalog.get("specs/SCHOTT-optical/N-BK7")
297
+ bk7 = page.load()
298
+
299
+ Material data is cached in a user data directory. Set
300
+ ``PYOPTIK_DATA_DIR`` to choose a different location.
301
+
302
+ The command-line interface provides the canonical catalog workflow:
303
+
304
+ .. code-block:: bash
305
+
306
+ pyoptik setup
307
+ python -m PyOptik download-all --data-root ./refractiveindex-data
308
+ python -m PyOptik download-all --source pages --workers 8
309
+
310
+ After installation, the equivalent console command is ``pyoptik
311
+ download-all --data-root ./refractiveindex-data``.
312
+
313
+ For a beginner-friendly first-time setup, use:
314
+
315
+ .. code-block:: bash
316
+
317
+ pyoptik setup
318
+
319
+ This downloads the complete upstream snapshot, preserves the original
320
+ hierarchy, and can be safely run again. Use ``pyoptik setup --force`` to
321
+ refresh the local snapshot.
322
+
323
+ The same setup is available from Python:
324
+
325
+ .. code-block:: python
326
+
327
+ from PyOptik import download_snapshot
328
+
329
+ catalog = download_snapshot()
330
+ silver = catalog.get("main/Ag/Johnson").load()
331
+
332
+ The default ``download-all`` command downloads one upstream snapshot and
333
+ extracts every material page locally, preserving its ``shelf/book/page``
334
+ hierarchy. This avoids thousands of individual HTTP requests. Existing
335
+ snapshots are reused; pass ``--force`` to refresh the snapshot. The
336
+ ``--source pages`` fallback downloads individual pages and supports bounded
337
+ parallelism with ``--workers``. ``--fail-fast`` stops after the first failed
338
+ page, while ``--verbose`` enables detailed diagnostics.
339
+
340
+ Common issues
341
+ -------------
342
+
343
+ * Attach units to wavelengths whenever possible.
344
+ * Use ``out_of_range="raise"`` when extrapolation would invalidate a result.
345
+ * Use the canonical catalog identifier when provenance or source selection
346
+ matters.
347
+ * Use ``MPLBACKEND=Agg`` for documentation builds, CI, and remote servers.
348
+ * If a material cannot be found, run ``pyoptik setup`` or call
349
+ ``download_snapshot()`` before loading its canonical page.
350
+
351
+ Development and testing
352
+ -----------------------
353
+
354
+ Clone the repository and install development dependencies:
355
+
356
+ .. code-block:: bash
357
+
358
+ git clone https://github.com/MartinPdeS/PyOptik.git
359
+ cd PyOptik
360
+ python -m pip install -e ".[testing,documentation,dev]"
361
+
362
+ Run the offline test suite:
363
+
364
+ .. code-block:: bash
365
+
366
+ MPLBACKEND=Agg pytest -m "not network"
367
+
368
+ Build the documentation:
369
+
370
+ .. code-block:: bash
371
+
372
+ MPLBACKEND=Agg sphinx-build -b html -W docs/source docs/build/html
373
+
374
+ Network-dependent tests are marked with ``network`` and are excluded from
375
+ normal CI runs.
376
+
377
+ Documentation and references
378
+ ----------------------------
379
+
380
+ * `Online documentation <https://martinpdes.github.io/PyOptik/>`_
381
+ * `RefractiveIndex.INFO database <https://github.com/polyanskiy/refractiveindex.info-database>`_
382
+ * `PyOptik source repository <https://github.com/MartinPdeS/PyOptik>`_
383
+
384
+ The material data is sourced from RefractiveIndex.INFO. Refer to each
385
+ material page for its original scientific or manufacturer reference.
386
+
387
+ .. |python| image:: https://img.shields.io/pypi/pyversions/pyoptik.svg
388
+ :alt: Supported Python versions
389
+ :target: https://pypi.org/project/pyoptik/
390
+
391
+ .. |logo| image:: https://github.com/MartinPdeS/PyOptik/raw/master/docs/images/logo.png
392
+ :alt: PyOptik logo
393
+
394
+ .. |docs| image:: https://github.com/martinpdes/pyoptik/actions/workflows/deploy_documentation.yml/badge.svg
395
+ :target: https://martinpdes.github.io/PyOptik/
396
+ :alt: Documentation status
397
+
398
+ .. |ci/cd| image:: https://github.com/martinpdes/pyoptik/actions/workflows/tests.yml/badge.svg
399
+ :target: https://github.com/MartinPdeS/PyOptik/actions/workflows/tests.yml
400
+ :alt: Continuous integration status
401
+
402
+ .. |coverage| image:: https://raw.githubusercontent.com/MartinPdeS/PyOptik/python-coverage-comment-action-data/badge.svg
403
+ :alt: Test coverage
404
+ :target: https://htmlpreview.github.io/?https://github.com/MartinPdeS/PyOptik/blob/python-coverage-comment-action-data/htmlcov/index.html
405
+
406
+ .. |PyPi| image:: https://badge.fury.io/py/pyoptik.svg
407
+ :alt: PyPI version
408
+ :target: https://pypi.org/project/pyoptik/
409
+
410
+ .. |PyPi_download| image:: https://img.shields.io/pypi/dm/pyoptik.svg
411
+ :alt: PyPI downloads
412
+ :target: https://pypistats.org/packages/pyoptik
413
+
414
+ .. |anaconda| image:: https://anaconda.org/martinpdes/pyoptik/badges/version.svg
415
+ :alt: Anaconda version
416
+ :target: https://anaconda.org/martinpdes/pyoptik
417
+
418
+ .. |anaconda_download| image:: https://anaconda.org/martinpdes/pyoptik/badges/downloads.svg
419
+ :alt: Anaconda downloads
420
+ :target: https://anaconda.org/martinpdes/pyoptik
@@ -4,14 +4,17 @@ try:
4
4
  except ImportError:
5
5
  __version__ = "0.0.0"
6
6
 
7
+ import logging
8
+
9
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
10
+
7
11
 
8
- from .material_bank import MaterialBank
9
12
  from .material_type import MaterialType
13
+ from .catalog import MaterialCatalog, MaterialId, MaterialPage, download_snapshot
10
14
 
11
15
  from .material import TabulatedMaterial
12
16
  from .material import SellmeierMaterial
13
17
  from .material import base_class
14
18
 
15
- Material = MaterialBank # For retro-compatibility
16
19
 
17
20
  TIMEOUT = 10 # Default timeout for requests in seconds
@@ -0,0 +1,124 @@
1
+ import argparse
2
+ import logging
3
+ from pathlib import Path
4
+ import sys
5
+
6
+ from PyOptik import MaterialCatalog
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def main() -> None:
12
+ """Run the PyOptik material-library command line interface.
13
+
14
+ The default command downloads the complete upstream material snapshot.
15
+ Use ``download-all`` for the explicit catalog download command or its
16
+ page-based fallback mode.
17
+ """
18
+ parser = argparse.ArgumentParser(
19
+ description="Set up or download the canonical PyOptik material catalog"
20
+ )
21
+ parser.add_argument(
22
+ "command",
23
+ nargs="?",
24
+ default="setup",
25
+ choices=("setup", "download-all"),
26
+ help="Canonical catalog command (default: setup)",
27
+ )
28
+ parser.add_argument(
29
+ "--verbose",
30
+ action="store_true",
31
+ help="Enable detailed download and material-loading logs",
32
+ )
33
+ parser.add_argument(
34
+ "--force",
35
+ action="store_true",
36
+ help="Re-download upstream pages that are already cached",
37
+ )
38
+ parser.add_argument(
39
+ "--fail-fast",
40
+ action="store_true",
41
+ help="Stop the complete upstream download at the first failed page",
42
+ )
43
+ parser.add_argument(
44
+ "--data-root",
45
+ type=Path,
46
+ default=None,
47
+ help="Root directory for the complete upstream catalog",
48
+ )
49
+ parser.add_argument(
50
+ "--source",
51
+ choices=("snapshot", "pages"),
52
+ default="snapshot",
53
+ help="Download source: one upstream snapshot (default) or individual pages",
54
+ )
55
+ parser.add_argument(
56
+ "--workers",
57
+ type=int,
58
+ default=8,
59
+ help="Concurrent page downloads when --source=pages (default: 8)",
60
+ )
61
+ parser.add_argument(
62
+ "--no-progress",
63
+ action="store_true",
64
+ help="Disable the snapshot download progress bar",
65
+ )
66
+ args = parser.parse_args()
67
+
68
+ logging.basicConfig(
69
+ level=logging.DEBUG if args.verbose else logging.INFO,
70
+ format="%(asctime)s %(levelname)s %(name)s: %(message)s",
71
+ )
72
+
73
+ if args.command == "setup" and args.source != "snapshot":
74
+ parser.error("setup always uses the upstream snapshot; use download-all for page mode")
75
+ source = "snapshot" if args.command == "setup" else args.source
76
+ logger.info(
77
+ "Setting up the upstream material catalog"
78
+ if args.command == "setup"
79
+ else "Updating the upstream material catalog"
80
+ )
81
+
82
+ def show_progress(downloaded: int, total: int) -> None:
83
+ """Render snapshot download progress on the terminal."""
84
+ width = 30
85
+ if total:
86
+ complete = min(width, int(width * downloaded / total))
87
+ bar = "=" * complete + ">" + " " * max(0, width - complete - 1)
88
+ percent = 100 * downloaded / total
89
+ message = (
90
+ f"\rDownloading snapshot [{bar}] {percent:5.1f}% "
91
+ f"({downloaded / 1024**2:.1f}/{total / 1024**2:.1f} MiB)"
92
+ )
93
+ else:
94
+ message = f"\rDownloading snapshot: {downloaded / 1024**2:.1f} MiB"
95
+ print(message, end="", file=sys.stderr, flush=True)
96
+
97
+ progress = None if args.no_progress else show_progress
98
+ if source == "snapshot":
99
+ catalog = MaterialCatalog.from_snapshot(
100
+ data_root=args.data_root,
101
+ force=args.force,
102
+ progress=progress,
103
+ )
104
+ else:
105
+ catalog = MaterialCatalog.from_upstream(data_root=args.data_root)
106
+ if progress:
107
+ print(file=sys.stderr)
108
+ logger.info(
109
+ "Finalizing the complete upstream material catalog"
110
+ if source == "snapshot"
111
+ else "Downloading the complete upstream material catalog"
112
+ )
113
+ if source == "snapshot":
114
+ logger.info("Snapshot download complete; material pages are already available locally")
115
+ return
116
+ catalog.download_all(
117
+ force=args.force,
118
+ continue_on_error=not args.fail_fast,
119
+ workers=args.workers,
120
+ )
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '2.0.7'
22
- __version_tuple__ = version_tuple = (2, 0, 7)
21
+ __version__ = version = '3.0.0'
22
+ __version_tuple__ = version_tuple = (3, 0, 0)
23
23
 
24
- __commit_id__ = commit_id = 'ga431e7115'
24
+ __commit_id__ = commit_id = 'g8275e8e4b'