-
Notifications
You must be signed in to change notification settings - Fork 3
/
volume_idealgas.py
161 lines (133 loc) · 4.46 KB
/
volume_idealgas.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
"""
Volume expansion for ideal gas (:mod:`~thermoextrap.volume_idealgas`)
=====================================================================
"""
from functools import lru_cache
from .docstrings import DOCFILLER_SHARED
from .models import Derivatives, ExtrapModel
docfiller_shared = DOCFILLER_SHARED.levels_to_top(
"cmomy", "xtrap", "beta", "volume"
).decorate
class VolumeDerivFuncsIG:
"""
Calculates specific derivative values at refV with data x and W.
Only go to first order for volume extrapolation.
Here W represents the virial instead of the potential energy.
"""
def __init__(self, refV=1.0) -> None: # noqa: N803
# If do not set refV, assumes virial data is already divided by the reference volume
# If this is not the case, need to set refV
# Or if need refV to also compute custom term, need to specify
self.refV = refV
def __getitem__(self, order):
# Check to make sure not going past first order
if order > 1:
raise ValueError(
"Volume derivatives cannot go past 1st order"
+ " and received %i" % order
+ "\n(because would need derivatives of forces)"
)
return self.create_deriv_func(order)
def create_deriv_func(self, order):
# Works only because of local scope
# Even if order is defined somewhere outside of this class, won't affect returned func
def func(W, xW): # noqa: N803
if order == 0:
# Zeroth order derivative
deriv_val = xW[0]
else:
# First order derivative
deriv_val = (xW[1] - xW[0] * W[1]) / (
self.refV
) # No 3 b/c our IG is 1D
# Term unique to Ideal Gas... <x>/L
# Replace with whatever is appropriate to observable of interest
deriv_val += xW[0] / self.refV
return deriv_val
return func
@lru_cache(5)
def factory_derivatives(refV=1.0): # noqa: N803
"""
Factory function to provide coefficients of expansion.
Parameters
----------
refV : float
reference volume (default 1 - if already divided by volume no need to set)
Returns
-------
derivatives : :class:`thermoextrap.models.Derivatives` object
Object used to calculate moments
"""
deriv_funcs = VolumeDerivFuncsIG(refV=refV)
return Derivatives(deriv_funcs)
def factory_extrapmodel(volume, uv, xv, order=1, alpha_name="volume", **kws):
"""
Factory function to create Extrapolation model for volume expansion.
Parameters
----------
order : int
maximum order
volume : float
reference value of volume
uv, xv : array-like
values for u and x
alpha_name, str, default='volume'
name of expansion parameter
kws : dict
extra arguments to `factory_datavalues`
Returns
-------
extrapmodel : ExtrapModel
"""
if order != 1:
msg = "only first order supported"
raise ValueError(msg)
from .data import factory_data_values
data = factory_data_values(
uv=uv, xv=xv, order=order, central=False, xalpha=False, **kws
)
derivatives = factory_derivatives(refV=volume)
return ExtrapModel(
alpha0=volume,
data=data,
derivatives=derivatives,
order=order,
minus_log=False,
alpha_name=alpha_name,
)
def factory_extrapmodel_data(volume, data, order=1, alpha_name="volume"):
"""
Factory function to create Extrapolation model for volume expansion.
Parameters
----------
volume : float
reference value of volume
data : object
Note that this data object should have central=False, deriv_dim=None
alpha_name, str, default='volume'
name of expansion parameter
Returns
-------
extrapmodel : ExtrapModel
"""
if order is None:
order = data.order
if order != 1:
msg = "only first order supported"
raise ValueError(msg)
if order > data.order:
raise ValueError
if data.central:
msg = "Only works with raw moments."
raise ValueError
if data.deriv is not None:
raise ValueError
derivatives = factory_derivatives(refV=volume)
return ExtrapModel(
alpha0=volume,
data=data,
derivatives=derivatives,
order=order,
minus_log=False,
alpha_name=alpha_name,
)