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.
- PyOptik/__init__.py +15 -0
- PyOptik/__main__.py +7 -0
- PyOptik/_version.py +16 -0
- PyOptik/data/__init__.py +0 -0
- PyOptik/data/sellmeier/BAK1.yml +60 -0
- PyOptik/data/sellmeier/BK7.yml +60 -0
- PyOptik/data/sellmeier/SF5.yml +54 -0
- PyOptik/data/sellmeier/ZBLAN.yml +10 -0
- PyOptik/data/sellmeier/air.yml +15 -0
- PyOptik/data/sellmeier/argon.yml +14 -0
- PyOptik/data/sellmeier/crown.yml +58 -0
- PyOptik/data/sellmeier/flint.yml +57 -0
- PyOptik/data/sellmeier/fused_silica.yml +15 -0
- PyOptik/data/sellmeier/germanium.yml +10 -0
- PyOptik/data/sellmeier/lithium_niobate.yml +10 -0
- PyOptik/data/sellmeier/polystyren.yml +14 -0
- PyOptik/data/sellmeier/silicon.yml +10 -0
- PyOptik/data/sellmeier/soda_lime_glass.yml +118 -0
- PyOptik/data/sellmeier/water.yml +14 -0
- PyOptik/data/tabulated/aluminium.yml +215 -0
- PyOptik/data/tabulated/calcium.yml +72 -0
- PyOptik/data/tabulated/copper.yml +58 -0
- PyOptik/data/tabulated/gold.yml +58 -0
- PyOptik/data/tabulated/iron.yml +58 -0
- PyOptik/data/tabulated/nickel.yml +58 -0
- PyOptik/data/tabulated/silver.yml +58 -0
- PyOptik/data/tabulated/sodium.yml +34 -0
- PyOptik/data/tabulated/zinc.yml +162 -0
- PyOptik/directories.py +44 -0
- PyOptik/libraries/__init__.py +0 -0
- PyOptik/libraries/classics.yml +28 -0
- PyOptik/libraries/dielectrics.yml +20 -0
- PyOptik/libraries/metals.yml +20 -0
- PyOptik/libraries/minimal.yml +9 -0
- PyOptik/libraries/organics.yml +11 -0
- PyOptik/libraries/others.yml +4 -0
- PyOptik/libraries/polymers.yml +8 -0
- PyOptik/material/__init__.py +3 -0
- PyOptik/material/base_class.py +90 -0
- PyOptik/material/sellmeier_class.py +198 -0
- PyOptik/material/tabulated_class.py +180 -0
- PyOptik/material_bank.py +466 -0
- PyOptik/material_type.py +9 -0
- PyOptik/units.py +56 -0
- PyOptik/utils.py +46 -0
- PyOptik-1.9.8.0.dist-info/LICENSE +21 -0
- PyOptik-1.9.8.0.dist-info/METADATA +295 -0
- PyOptik-1.9.8.0.dist-info/RECORD +50 -0
- PyOptik-1.9.8.0.dist-info/WHEEL +5 -0
- 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
|
+
)
|
PyOptik/material_bank.py
ADDED
|
@@ -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()
|