PyOptik 1.9.8__0-py3-none-any.whl

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 (50) hide show
  1. PyOptik/__init__.py +15 -0
  2. PyOptik/__main__.py +7 -0
  3. PyOptik/_version.py +16 -0
  4. PyOptik/data/__init__.py +0 -0
  5. PyOptik/data/sellmeier/BAK1.yml +60 -0
  6. PyOptik/data/sellmeier/BK7.yml +60 -0
  7. PyOptik/data/sellmeier/SF5.yml +54 -0
  8. PyOptik/data/sellmeier/ZBLAN.yml +10 -0
  9. PyOptik/data/sellmeier/air.yml +15 -0
  10. PyOptik/data/sellmeier/argon.yml +14 -0
  11. PyOptik/data/sellmeier/crown.yml +58 -0
  12. PyOptik/data/sellmeier/flint.yml +57 -0
  13. PyOptik/data/sellmeier/fused_silica.yml +15 -0
  14. PyOptik/data/sellmeier/germanium.yml +10 -0
  15. PyOptik/data/sellmeier/lithium_niobate.yml +10 -0
  16. PyOptik/data/sellmeier/polystyren.yml +14 -0
  17. PyOptik/data/sellmeier/silicon.yml +10 -0
  18. PyOptik/data/sellmeier/soda_lime_glass.yml +118 -0
  19. PyOptik/data/sellmeier/water.yml +14 -0
  20. PyOptik/data/tabulated/aluminium.yml +215 -0
  21. PyOptik/data/tabulated/calcium.yml +72 -0
  22. PyOptik/data/tabulated/copper.yml +58 -0
  23. PyOptik/data/tabulated/gold.yml +58 -0
  24. PyOptik/data/tabulated/iron.yml +58 -0
  25. PyOptik/data/tabulated/nickel.yml +58 -0
  26. PyOptik/data/tabulated/silver.yml +58 -0
  27. PyOptik/data/tabulated/sodium.yml +34 -0
  28. PyOptik/data/tabulated/zinc.yml +162 -0
  29. PyOptik/directories.py +44 -0
  30. PyOptik/libraries/__init__.py +0 -0
  31. PyOptik/libraries/classics.yml +28 -0
  32. PyOptik/libraries/dielectrics.yml +20 -0
  33. PyOptik/libraries/metals.yml +20 -0
  34. PyOptik/libraries/minimal.yml +9 -0
  35. PyOptik/libraries/organics.yml +11 -0
  36. PyOptik/libraries/others.yml +4 -0
  37. PyOptik/libraries/polymers.yml +8 -0
  38. PyOptik/material/__init__.py +3 -0
  39. PyOptik/material/base_class.py +90 -0
  40. PyOptik/material/sellmeier_class.py +198 -0
  41. PyOptik/material/tabulated_class.py +180 -0
  42. PyOptik/material_bank.py +466 -0
  43. PyOptik/material_type.py +9 -0
  44. PyOptik/units.py +56 -0
  45. PyOptik/utils.py +46 -0
  46. PyOptik-1.9.8.0.dist-info/LICENSE +21 -0
  47. PyOptik-1.9.8.0.dist-info/METADATA +295 -0
  48. PyOptik-1.9.8.0.dist-info/RECORD +50 -0
  49. PyOptik-1.9.8.0.dist-info/WHEEL +5 -0
  50. PyOptik-1.9.8.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import numpy
5
+ from pydantic.dataclasses import dataclass, Field, ConfigDict
6
+ from typing import Optional, Union
7
+ import yaml
8
+ from PyOptik.directories import tabulated_data_path
9
+ from PyOptik.material.base_class import BaseMaterial
10
+ from MPSPlots.styles import mps
11
+ import matplotlib.pyplot as plt
12
+ from PyOptik.units import Quantity, micrometer, meter
13
+
14
+
15
+ config_dict = ConfigDict(
16
+ arbitrary_types_allowed=True
17
+ )
18
+
19
+
20
+ @dataclass(config=config_dict, slots=True, eq=False)
21
+ class TabulatedMaterial(BaseMaterial):
22
+ """
23
+ Class representing a material with tabulated refractive index (n) and absorption (k) values.
24
+
25
+ Attributes
26
+ ----------
27
+ filename : str
28
+ The name of the YAML file containing material properties.
29
+ wavelength : numpy.ndarray
30
+ Array of wavelengths in micrometers for which the refractive index and absorption values are tabulated.
31
+ n_values : numpy.ndarray
32
+ Array of tabulated refractive index values (n) corresponding to the wavelengths.
33
+ k_values : numpy.ndarray
34
+ Array of tabulated absorption values (k) corresponding to the wavelengths.
35
+ reference : Optional[str]
36
+ Reference information for the material data.
37
+ """
38
+ filename: str
39
+ wavelength: numpy.ndarray = Field(init=False)
40
+ n_values: numpy.ndarray = Field(init=False)
41
+ k_values: numpy.ndarray = Field(init=False)
42
+ reference: Optional[str] = Field(init=False)
43
+
44
+ def __repr__(self) -> str:
45
+ return self.__str__()
46
+
47
+ def __str__(self) -> str:
48
+ return self.filename
49
+
50
+ def __post_init__(self) -> None:
51
+ """
52
+ Post-initialization method to load the tabulated data from the YAML file.
53
+ """
54
+ self._load_tabulated_data()
55
+
56
+ def _load_tabulated_data(self) -> None:
57
+ """
58
+ Loads the tabulated refractive index and absorption values from the specified YAML file.
59
+
60
+ Raises
61
+ ------
62
+ FileNotFoundError
63
+ If the specified YAML file does not exist.
64
+ ValueError
65
+ If the YAML data is malformed or missing required keys.
66
+ """
67
+ file_path = tabulated_data_path / f'{self.filename}'
68
+
69
+ if not file_path.with_suffix('.yml').exists():
70
+ raise FileNotFoundError(f"YAML file {file_path} not found.")
71
+
72
+ with file_path.with_suffix('.yml').open('r') as file:
73
+ parsed_yaml = yaml.safe_load(file)
74
+
75
+ try:
76
+ # Extract data points
77
+ data_points = parsed_yaml['DATA'][0]['data'].strip().split('\n')
78
+ data = numpy.array([[float(value) for value in point.split()] for point in data_points])
79
+
80
+ self.wavelength = data[:, 0] * micrometer
81
+ self.n_values = data[:, 1]
82
+ self.k_values = data[:, 2]
83
+ except (KeyError, IndexError, ValueError):
84
+ raise ValueError(f"Invalid or missing data in YAML file {file_path}")
85
+
86
+ self.wavelength_bound = [self.wavelength.min().magnitude, self.wavelength.max().magnitude] * micrometer
87
+
88
+ # Extract reference
89
+ self.reference = parsed_yaml.get('REFERENCES', None)
90
+
91
+ @BaseMaterial.ensure_units
92
+ def compute_refractive_index(self, wavelength: Union[float, Quantity]) -> numpy.ndarray:
93
+ """
94
+ Interpolates the refractive index (n) and absorption (k) values for the given wavelength(s).
95
+
96
+ Parameters
97
+ ----------
98
+ wavelength : Union[float, Quantity]
99
+ Wavelength(s) in micrometers for which to interpolate n and k.
100
+
101
+ Returns
102
+ -------
103
+ numpy.ndarray
104
+ Complex refractive index values (n + i*k) for the given wavelength(s).
105
+
106
+ Raises
107
+ ------
108
+ ValueError
109
+ If the wavelength is outside the tabulated range.
110
+ """
111
+ return_as_scalar = numpy.isscalar(wavelength.magnitude)
112
+
113
+ wavelength = numpy.atleast_1d(wavelength)
114
+
115
+ self._check_wavelength(wavelength)
116
+
117
+ n_interp = numpy.interp(wavelength.to(meter).magnitude, self.wavelength.to(meter).magnitude, self.n_values)
118
+ k_interp = numpy.interp(wavelength.to(meter).magnitude, self.wavelength.to(meter).magnitude, self.k_values)
119
+
120
+ index = n_interp + 1j * k_interp
121
+
122
+ return index[0] if return_as_scalar else index
123
+
124
+ @BaseMaterial.ensure_units
125
+ def plot(self, wavelength: Optional[Quantity] = None) -> None:
126
+ """
127
+ Plots the tabulated refractive index (n) and absorption (k) as a function of wavelength.
128
+
129
+ Parameters
130
+ ----------
131
+ wavelength : Optional[Quantity]
132
+ The range of wavelengths to plot, in micrometers. If not provided, the entire tabulated wavelength range is used.
133
+ title : str, optional
134
+ Title of the plot.
135
+ grid : bool, optional
136
+ Whether to show grid lines on the plot.
137
+
138
+ Raises
139
+ ------
140
+ ValueError
141
+ If the wavelength is not a 1D array or list of float values.
142
+ """
143
+ n_values, k_values = self.compute_refractive_index(wavelength).real, self.compute_refractive_index(wavelength).imag
144
+
145
+ with plt.style.context(mps):
146
+ _, ax1 = plt.subplots()
147
+
148
+ ax1.set(
149
+ title=f"Refractive Index and Absorption vs. Wavelength [{self.filename}]",
150
+ xlabel='Wavelength [µm]',
151
+ ylabel='Refractive Index (n)',
152
+ )
153
+
154
+ ax1.plot(wavelength.to(micrometer).magnitude, n_values, 'o-', color='tab:blue', label='n')
155
+
156
+ ax2 = ax1.twinx()
157
+
158
+ ax2.set(
159
+ xlabel='Wavelength [µm]',
160
+ ylabel='Absorption (k)',
161
+ )
162
+
163
+ ax2.plot(wavelength.to(micrometer).magnitude, k_values, 'o-', color='tab:red', label='k')
164
+
165
+ plt.show()
166
+
167
+ def print(self) -> str:
168
+ """
169
+ Provides a formal string representation of the TabulatedMaterial object, including key attributes.
170
+
171
+ Returns
172
+ -------
173
+ str
174
+ Formal representation of the TabulatedMaterial object.
175
+ """
176
+ return (
177
+ f"\nTabulatedMaterial: '{self.filename}',\n"
178
+ f"wavelength_range: [{self.wavelength.min()} µm, {self.wavelength.max()} µm],\n"
179
+ f"reference: '{self.reference}')"
180
+ )
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import numpy
5
+ import os
6
+ import re
7
+ import yaml
8
+ import logging
9
+ from pathlib import Path
10
+ from typing import List, Union, Optional, Tuple
11
+ from PyOptik.directories import data_path, libraries_path
12
+ from PyOptik.material.sellmeier_class import SellmeierMaterial
13
+ from PyOptik.material.tabulated_class import TabulatedMaterial
14
+ from PyOptik.utils import download_yml_file
15
+ from tabulate import tabulate
16
+ from dataclasses import dataclass
17
+ from PyOptik.material_type import MaterialType
18
+
19
+
20
+ @dataclass(frozen=True, slots=True)
21
+ class _MaterialBank():
22
+ """
23
+ A class representing a centralized material bank for common optical materials available in the PyOptik library.
24
+
25
+ The `_MaterialBank` class provides access to a predefined list of materials used in optical simulations,
26
+ categorized into Sellmeier and Tabulated materials. It allows users to dynamically retrieve materials
27
+ based on their names without the need to instantiate the class. The material bank can be expanded
28
+ or modified by adding or removing materials from the bank, and it provides utilities to fetch material data
29
+ dynamically when accessed as class attributes.
30
+
31
+ Attributes
32
+ ----------
33
+ all : list
34
+ A combined list of all materials, including both Sellmeier and Tabulated materials.
35
+
36
+ Usage
37
+ -----
38
+ Materials can be accessed directly as class attributes:
39
+
40
+ >>> material = _MaterialBank
41
+ >>> bk7_material = material.BK7 # Dynamically retrieves the BK7 material.
42
+
43
+ To add a new material to the Sellmeier bank:
44
+
45
+ >>> material.add_sellmeier_to_bank("new_material.yml", "https://refractiveindex.info/database/data-nk/main/SiO2/Malitson.yml")
46
+
47
+ To remove a material from the bank:
48
+
49
+ >>> MaterialBank.remove_item("obsolete_material.yml")
50
+
51
+ Raises
52
+ ------
53
+ FileNotFoundError
54
+ If a material is not found in either the Sellmeier or Tabulated material lists.
55
+ """
56
+
57
+ use_tabulated: bool = True
58
+ use_sellmeier: bool = True
59
+
60
+ def __getattr__(self, material_name: str) -> Union[SellmeierMaterial, TabulatedMaterial]:
61
+ """
62
+ Retrieve a material by name dynamically at the class level, respecting filter options.
63
+
64
+ Parameters
65
+ ----------
66
+ material_name : str
67
+ The name of the material to retrieve.
68
+
69
+ Returns
70
+ -------
71
+ Union[SellmeierMaterial, TabulatedMaterial]
72
+ An instance of the material if found.
73
+
74
+ Raises
75
+ ------
76
+ AttributeError
77
+ If the material is not found in the filtered or unfiltered lists.
78
+ """
79
+ # Apply the filtering logic based on class-level attributes
80
+ if material_name in self.sellmeier:
81
+ return SellmeierMaterial(filename=material_name)
82
+ elif material_name in self.tabulated:
83
+ return TabulatedMaterial(filename=material_name)
84
+
85
+ raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{material_name}'")
86
+
87
+ def get(self, material_name: str) -> Union[SellmeierMaterial, TabulatedMaterial]:
88
+ """
89
+ Retrieve a material by name, respecting filter options.
90
+
91
+ Parameters
92
+ ----------
93
+ material_name : str
94
+ The name of the material to retrieve.
95
+
96
+ Returns
97
+ -------
98
+ Union[SellmeierMaterial, TabulatedMaterial]
99
+ An instance of the material if found.
100
+
101
+ Raises
102
+ ------
103
+ AttributeError
104
+ If the material is not found in the filtered or unfiltered lists.
105
+ """
106
+ return self.__getattr__(material_name)
107
+
108
+ @classmethod
109
+ def set_filter(cls, use_tabulated: bool = False, use_sellmeier: bool = False) -> None:
110
+ """
111
+ Set the filter for the MaterialBank.
112
+
113
+ Parameters
114
+ ----------
115
+ use_tabulated : bool
116
+ If True, restricts retrieval to tabulated materials only.
117
+ use_sellmeier : bool
118
+ If True, restricts retrieval to sellmeier materials only.
119
+
120
+ Raises
121
+ ------
122
+ ValueError
123
+ If both use_tabulated and use_sellmeier are set to True.
124
+ """
125
+ if not use_tabulated and not use_sellmeier:
126
+ raise ValueError("Cannot set both 'use_tabulated' and 'use_sellmeier' to False.")
127
+
128
+ cls.use_tabulated = use_tabulated
129
+ cls.use_sellmeier = use_sellmeier
130
+
131
+ def _list_materials(self, material_type: MaterialType) -> List[str]:
132
+ """create_sellmeier_file
133
+ Helper method to list materials of a specific type.
134
+
135
+ Parameters
136
+ ----------
137
+ material_type : MaterialType
138
+ The type of materials to list (MaterialType.SELLMEIER or MaterialType.TABULATED).
139
+
140
+ Returns
141
+ -------
142
+ List[str]
143
+ A list of material names of the specified type.
144
+ """
145
+ directory = data_path / material_type.value
146
+ return [
147
+ os.path.splitext(f)[0] for f in os.listdir(directory) if os.path.isfile(directory / f) and f.endswith('.yml')
148
+ ]
149
+
150
+ @property
151
+ def sellmeier(self) -> List[str]:
152
+ """
153
+ List all available Sellmeier materials.
154
+
155
+ Returns
156
+ -------
157
+ List[str]
158
+ A list of all Sellmeier material names.
159
+ """
160
+ return self._list_materials(MaterialType.SELLMEIER) if self.use_sellmeier else []
161
+
162
+ @property
163
+ def tabulated(self) -> List[str]:
164
+ """
165
+ List all available Tabulated materials.
166
+
167
+ Returns
168
+ -------
169
+ List[str]
170
+ A list of all Tabulated material names.
171
+ """
172
+ return self._list_materials(MaterialType.TABULATED) if self.use_tabulated else []
173
+
174
+ @property
175
+ def all(self) -> List[str]:
176
+ """
177
+ List all available materials, including both Sellmeier and Tabulated materials.
178
+
179
+ Returns
180
+ -------
181
+ List[str]
182
+ A combined list of all Sellmeier and Tabulated material names.
183
+ """
184
+ return self.sellmeier + self.tabulated
185
+
186
+ def print_available(cls) -> None:
187
+ """
188
+ Prints out all the available Sellmeier and Tabulated materials in a tabulated format.
189
+ """
190
+ sellmeier_materials = cls.sellmeier
191
+ tabulated_materials = cls.tabulated
192
+
193
+ # Create data for the table
194
+ table_data = []
195
+ max_len = max(len(sellmeier_materials), len(tabulated_materials))
196
+ for i in range(max_len):
197
+ sellmeier = sellmeier_materials[i] if i < len(sellmeier_materials) else ""
198
+ tabulated = tabulated_materials[i] if i < len(tabulated_materials) else ""
199
+ table_data.append([sellmeier, tabulated])
200
+
201
+ # Define headers
202
+ headers = ["Sellmeier Materials", "Tabulated Materials"]
203
+
204
+ # Print the table using tabulate
205
+ print(tabulate(table_data, headers=headers, tablefmt="grid"))
206
+
207
+ @classmethod
208
+ def add_material_to_bank(cls, filename: str, url: str, material_type: MaterialType) -> None:
209
+ """
210
+ Add a material to the material bank.
211
+
212
+ Downloads a YAML file containing the material data from a specified URL and stores it
213
+ in the specified materials directory.
214
+
215
+ Parameters
216
+ ----------
217
+ filename : str
218
+ The name of the file to be saved in the material bank.
219
+ url : str
220
+ The URL from where the material file is downloaded.
221
+ material_type : MaterialType
222
+ The type of material (MaterialType.SELLMEIER or MaterialType.TABULATED).
223
+
224
+ Returns
225
+ -------
226
+ None
227
+ """
228
+ if material_type not in [MaterialType.SELLMEIER, MaterialType.TABULATED]:
229
+ raise ValueError("Invalid material type. Please choose MaterialType.SELLMEIER or MaterialType.TABULATED.")
230
+
231
+ return download_yml_file(filename=filename, url=url, location=material_type)
232
+
233
+ @classmethod
234
+ def add_sellmeier_to_bank(cls, filename: str, url: str) -> None:
235
+ return cls.add_material_to_bank(filename=filename, url=url, material_type=MaterialType.SELLMEIER)
236
+
237
+ @classmethod
238
+ def add_tabulated_to_bank(cls, filename: str, url: str) -> None:
239
+ return cls.add_material_to_bank(filename=filename, url=url, material_type=MaterialType.TABULATED)
240
+
241
+ @classmethod
242
+ def remove_item(cls, filename: str, location: Union[str, MaterialType] = 'any') -> None:
243
+ """
244
+ Remove a file associated with a given element name from the specified location.
245
+
246
+ Parameters
247
+ ----------
248
+ filename : str
249
+ The name of the file to remove, without the '.yml' suffix.
250
+ location : Union[str, MaterialType]
251
+ The location to search for the file, either 'sellmeier', 'tabulated', 'any', or a MaterialType enum (default is 'any').
252
+
253
+ Raises
254
+ ------
255
+ FileNotFoundError
256
+ If the specified file does not exist.
257
+ ValueError
258
+ If an invalid location is provided.
259
+ """
260
+ if isinstance(location, MaterialType):
261
+ location = location.value
262
+
263
+ location = location.lower()
264
+
265
+ if location not in ['any', 'sellmeier', 'tabulated']:
266
+ raise ValueError("Invalid location. Please choose 'sellmeier', 'tabulated', or 'any'.")
267
+
268
+ if location in ['any', 'sellmeier']:
269
+ sellmeier_file = data_path / 'sellmeier' / f"{filename}.yml"
270
+ if sellmeier_file.exists():
271
+ sellmeier_file.unlink()
272
+
273
+ if location in ['any', 'tabulated']:
274
+ tabulated_file = data_path / 'tabulated' / f"{filename}.yml"
275
+ if tabulated_file.exists():
276
+ tabulated_file.unlink()
277
+
278
+ def clean_data_files(self, regex: str, location: Union[str, MaterialType] = 'any') -> None:
279
+ """
280
+ Remove all files matching the given regex from the specified location.
281
+
282
+ Parameters
283
+ ----------
284
+ regex : str
285
+ The regex pattern to match the filenames (without the '.yml' suffix).
286
+ location : Union[str, MaterialType]
287
+ The location to search for files, either 'sellmeier', 'tabulated', or 'any' (default is 'any').
288
+
289
+ Raises
290
+ ------
291
+ ValueError
292
+ If an invalid location is provided.
293
+ """
294
+ if isinstance(location, MaterialType):
295
+ location = location.value
296
+
297
+ location = location.lower()
298
+
299
+ if location not in ['any', 'sellmeier', 'tabulated']:
300
+ raise ValueError("Invalid location. Please choose 'sellmeier', 'tabulated', or 'any'.")
301
+
302
+ # Compile the regex pattern
303
+ pattern = re.compile(regex)
304
+
305
+ # Function to remove matching files in a given directory
306
+ def remove_matching_files(directory: Path):
307
+ for file in directory.glob("*.yml"):
308
+ if pattern.match(file.stem):
309
+ logging.info(f"Removing file: {file}")
310
+ file.unlink()
311
+
312
+ # Remove files from the sellmeier location if specified
313
+ if location in ['any', 'sellmeier']:
314
+ remove_matching_files(data_path / 'sellmeier')
315
+
316
+ # Remove files from the tabulated location if specified
317
+ if location in ['any', 'tabulated']:
318
+ remove_matching_files(data_path / 'tabulated')
319
+
320
+ def build_library(self, library: Union[str, List[str]] = 'classics', remove_previous: bool = False) -> None:
321
+ """
322
+ Downloads and saves materials data from the specified URLs.
323
+
324
+ Parameters
325
+ ----------
326
+ library : str | list[str]
327
+ The name or list of names of the libraries to download.
328
+ remove_previous : bool
329
+ If True, removes existing files before downloading new ones.
330
+ """
331
+ AVAILABLE_LIBRARIES = [os.path.splitext(f)[0] for f in os.listdir(libraries_path) if f.endswith('.yml')]
332
+
333
+ libraries_to_download = AVAILABLE_LIBRARIES if library == 'all' else set(numpy.atleast_1d(library))
334
+
335
+ # Ensure the requested library exists
336
+ assert libraries_to_download.issubset(AVAILABLE_LIBRARIES), f"Library value should be in {AVAILABLE_LIBRARIES}"
337
+
338
+ # Remove previous files if the flag is set
339
+ if remove_previous:
340
+ logging.info("Removing previous files from the library.")
341
+ self.clean_data_files(regex=".*", location="sellmeier") # Remove all sellmeier files
342
+ self.clean_data_files(regex=".*", location="tabulated") # Remove all tabulated files
343
+
344
+ for lib in libraries_to_download:
345
+ file_path = libraries_path / lib
346
+ with open(file_path.with_suffix('.yml'), 'r') as file:
347
+ data_dict = yaml.safe_load(file)
348
+
349
+ # Download new files for sellmeier
350
+ if data_dict.get('sellmeier', False):
351
+ for element_name, url in data_dict['sellmeier'].items():
352
+ download_yml_file(url=url, filename=element_name, location=MaterialType.SELLMEIER)
353
+
354
+ # Download new files for tabulated
355
+ if data_dict.get('tabulated', False):
356
+ for element_name, url in data_dict['tabulated'].items():
357
+ download_yml_file(url=url, filename=element_name, location=MaterialType.TABULATED)
358
+
359
+ def create_sellmeier_file(
360
+ self,
361
+ filename: str,
362
+ formula_type: int,
363
+ coefficients: List[float],
364
+ wavelength_range: Optional[Tuple[float, float]] = None,
365
+ reference: Optional[str] = None,
366
+ comments: Optional[str] = None,
367
+ specs: Optional[dict] = None) -> None:
368
+ """
369
+ Creates a YAML file with custom Sellmeier coefficients in the correct format.
370
+
371
+ Parameters
372
+ ----------
373
+ filename : str
374
+ The name of the file to create (without the extension).
375
+ formula_type : int
376
+ The type of Sellmeier formula.
377
+ coefficients : list[float]
378
+ A list of coefficients for the Sellmeier equation.
379
+ wavelength_range : Tuple[float, float]
380
+ The range of wavelengths, in micrometers.
381
+ reference : str
382
+ A reference for the material data.
383
+ comments : Optional[str]
384
+ Additional comments about the material.
385
+ specs : Optional[dict]
386
+ Additional specifications, such as temperature and whether the wavelength is in a vacuum.
387
+ """
388
+ reference = 'None' if reference is None else reference
389
+
390
+ # Create the data dictionary for YAML
391
+ data = {}
392
+ data['REFERENCES'] = reference
393
+ data['DATA'] = dict(
394
+ type=f'formula {formula_type}',
395
+ coefficients=" ".join(map(str, coefficients))
396
+ )
397
+
398
+ if wavelength_range is not None:
399
+ min_bound, max_bound = wavelength_range
400
+ data['DATA'].update({'wavelength_range': f"{min_bound} {max_bound}"})
401
+
402
+ data['DATA'] = [data['DATA']]
403
+ # Add comments if provided
404
+ if comments:
405
+ data['COMMENTS'] = comments
406
+
407
+ # Add specs if provided
408
+ if specs:
409
+ data['SPECS'] = specs
410
+
411
+ # Define the file path
412
+ file_path = data_path / 'sellmeier' / f"{filename}.yml"
413
+
414
+ # Write the data to a YAML file
415
+ with open(file_path, 'w') as file:
416
+ yaml.dump(data, file, default_flow_style=False)
417
+
418
+ logging.info(f"Sellmeier data saved to {file_path}")
419
+
420
+ def create_tabulated_file(
421
+ self,
422
+ filename: str,
423
+ data: List[Tuple[float, float, float]],
424
+ reference: Optional[str] = None,
425
+ comments: Optional[str] = None) -> None:
426
+ """
427
+ Creates a YAML file with tabulated nk data in the correct format.
428
+
429
+ Parameters
430
+ ----------
431
+ filename : str)
432
+ The name of the file to create (without the extension).
433
+ data : List[Tuple[float, float, float]])
434
+ The tabulated nk data.
435
+ reference : Optional[str])
436
+ A reference for the material data.
437
+ comments : Optional[str])
438
+ Additional comments about the material.
439
+ """
440
+ reference = 'None' if reference is None else reference
441
+
442
+ # Convert the data list to a formatted string
443
+ data_str = "\n".join(" ".join(map(str, row)) for row in data)
444
+
445
+ # Create the data dictionary for YAML
446
+ yaml_data = {}
447
+ yaml_data['REFERENCES'] = reference
448
+ yaml_data['DATA'] = [
449
+ dict(type='tabulated nk', data=data_str)
450
+ ]
451
+
452
+ # Add comments if provided
453
+ if comments:
454
+ yaml_data['COMMENTS'] = comments
455
+
456
+ # Define the file path
457
+ file_path = data_path / 'tabulated' / f"{filename}.yml"
458
+
459
+ # Write the data to a YAML file
460
+ with open(file_path, 'w') as file:
461
+ yaml.dump(yaml_data, file, default_flow_style=False)
462
+
463
+ logging.info(f"Tabulated nk data saved to {file_path}")
464
+
465
+
466
+ MaterialBank = _MaterialBank()
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from enum import Enum
5
+
6
+
7
+ class MaterialType(Enum):
8
+ SELLMEIER = "sellmeier"
9
+ TABULATED = "tabulated"