-
Notifications
You must be signed in to change notification settings - Fork 0
/
BoltzmannDistribution.py
309 lines (243 loc) · 9.73 KB
/
BoltzmannDistribution.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import numpy as np
from scipy import constants
from scipy.optimize import fsolve
from collections import OrderedDict
import json
import os
from typing import Dict, Union
from pathlib import Path
class BoltzmannDistribution:
"""
A class representing the Boltzmann distribution through a single representative point.
This class provides methods to calculate various properties of the distribution at
different temperatures.
Attributes:
SPEED_OF_LIGHT (float): Speed of light in m/s
representative_point (float): The 50% point at 5000K reference temperature
reference_temperature (float): Reference temperature in Kelvin
cache_size (int): Maximum number of entries in the energy ratio cache
cache (OrderedDict): LRU cache for energy ratios
cache_path (Path): Path to the cache file
"""
SPEED_OF_LIGHT = 299792458 # m/s
def __init__(self,
reference_temperature: float = 5000,
cache_size: int = 10000,
cache_filename: str = "boltzmann_cache.json"):
"""
Initialize the BoltzmannDistribution instance.
Args:
reference_temperature (float): Reference temperature in Kelvin
cache_size (int): Maximum number of entries in the cache
cache_filename (str): Name of the file to store the cache
"""
self.representative_point = 4.7849648084645400e-20 # 50% point at 5000K
self.reference_temperature = reference_temperature
self.cache_size = cache_size
self.cache: OrderedDict = OrderedDict()
self.cache_path = Path(cache_filename)
self._load_cache()
def scale_to_temperature(self, temperature: float) -> float:
"""
Scale a value from reference temperature to target temperature.
Args:
temperature (float): Target temperature in Kelvin
Returns:
float: Scaling factor
"""
return temperature / self.reference_temperature
def point_energy(self, temperature: float) -> float:
"""
Calculate the point energy at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Point energy
"""
return self.representative_point * self.scale_to_temperature(temperature)
def peak_energy(self, temperature: float) -> float:
"""
Calculate the peak energy of the distribution at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Peak energy
"""
return self.point_energy(temperature) / 0.245671510374651163
def area_under_curve_blackbody_energy(self, T):
"""
Calculate the black body energy at a temperature using standard formula converted to using these same scaling factors.
I did it this way to put it in line with the other formulas I am decomposing in this way to scaling factors
Same as sigma * T**4
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Radient black body energy
"""
return T**4 * 10e-8/1.763551974009425578
def area_under_curve(self, temperature: float) -> float:
"""
Calculate the area under the distribution curve at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Area under the curve
"""
return (self.point_energy(temperature) * temperature *
10e-22 / 50.20444590190353665093425661)
def peak_frequency(self, temperature: float) -> float:
"""
Calculate the peak frequency of the distribution at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Peak frequency
"""
return self.point_energy(temperature) * 10e32 / 0.1627836661598892
def fwhm(self, temperature: float) -> float:
"""
Calculate the Full Width at Half Maximum (FWHM) at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: FWHM value
"""
return self.point_energy(temperature) /0.162935868553486713
def entropy(self, temperature: float) -> float:
"""
Calculate the entropy of the system at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Entropy of the system
"""
# seems non linear
return self.point_energy(temperature) /temperature
def partition_function(self, temperature: float) -> float:
"""
Calculate the partition function at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Partition function value
"""
return self.average_energy(temperature) /1.50
def average_energy(self, temperature: float) -> float:
"""
Calculate the average energy of the system at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Average energy
"""
return self.point_energy(temperature) / .462098120373296908
def heat_capacity(self, temperature: float) -> float:
"""
Calculate the heat capacity of the system at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Heat capacity
"""
return self.average_energy(temperature) * temperature /( temperature *10)**2 *100
def free_energy(self, temperature: float) -> float:
"""
Calculate the Helmholtz free energy of the system at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Helmholtz free energy
"""
# had to revert to traditional method to handle this, non linear
k = constants.Boltzmann
return -k * temperature * np.log(k * temperature)
#def energy_distribution(self, temperature: float, energy: float) -> float:
#"""
#Calculate the probability of a state with a given energy at a specific temperature.
#
#Args:
#temperature (float): Temperature in Kelvin
#energy (float): Energy of the state
#
#Returns:
#float: Probability of the state
#"""
#return self.average_energy(temperature)
def most_probable_energy(self, temperature: float) -> float:
"""
Calculate the most probable energy at a given temperature.
Args:
temperature (float): Temperature in Kelvin
Returns:
float: Most probable energy
"""
return self.point_energy(temperature) / 1.386294361119890937
@staticmethod
def wavelength_from_frequency(frequency: float) -> float:
"""
Convert frequency to wavelength using the speed of light.
Args:
frequency (float): Frequency in Hz
Returns:
float: Wavelength in meters
"""
return BoltzmannDistribution.SPEED_OF_LIGHT / frequency
@staticmethod
def frequency_from_wavelength(wavelength: float) -> float:
"""
Convert wavelength to frequency using the speed of light.
Args:
wavelength (float): Wavelength in meters
Returns:
float: Frequency in Hz
"""
return BoltzmannDistribution.SPEED_OF_LIGHT / wavelength
def energy_at_percentage(self, temperature: float, percentage: float) -> float:
"""
Calculate the energy at a given percentage of the distribution.
Args:
temperature (float): Temperature in Kelvin
percentage (float): Percentage point to calculate (between 0 and 1)
Returns:
float: Energy at the specified percentage
"""
k = constants.Boltzmann
def equation(E):
return np.exp(-E / (k * temperature)) - percentage
initial_guess = k * temperature
return float(fsolve(equation, initial_guess)[0])
def get_energy_ratio(self, percentage: float, temperature: float) -> float:
"""
Get the energy ratio for a given percentage and temperature, using caching.
Args:
percentage (float): Percentage point to calculate
temperature (float): Temperature in Kelvin
Returns:
float: Energy ratio
"""
if percentage in self.cache:
print (" ", end='')
reference_ratio = self.cache[percentage]
self.cache.move_to_end(percentage)
else:
print ("*", end='')
reference_ratio = self.energy_at_percentage(
self.reference_temperature, percentage)
self.cache[percentage] = reference_ratio
if len(self.cache) > self.cache_size:
self.cache.popitem(last=False)
return reference_ratio * self.scale_to_temperature(temperature)
def _load_cache(self) -> None:
"""Load the cache from disk if it exists."""
if self.cache_path.exists():
with self.cache_path.open('r') as f:
loaded_cache = json.load(f)
self.cache = OrderedDict(
(float(k), v) for k, v in loaded_cache.items())
def save_cache(self) -> None:
"""Save the current cache to disk."""
with self.cache_path.open('w') as f:
json.dump({str(k): v for k, v in self.cache.items()}, f)
def __del__(self):
"""Save the cache when the object is destroyed."""
self.save_cache()