qupled 0.0.2__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.
- qupled-0.0.2/MANIFEST.in +2 -0
- qupled-0.0.2/PKG-INFO +27 -0
- qupled-0.0.2/pyproject.toml +40 -0
- qupled-0.0.2/qupled/Darwin/qupled.so +0 -0
- qupled-0.0.2/qupled/Linux/qupled.so +0 -0
- qupled-0.0.2/qupled/__init__.py +9 -0
- qupled-0.0.2/qupled/classic.py +489 -0
- qupled-0.0.2/qupled/quantum.py +300 -0
- qupled-0.0.2/qupled/util.py +330 -0
- qupled-0.0.2/qupled.egg-info/PKG-INFO +27 -0
- qupled-0.0.2/qupled.egg-info/SOURCES.txt +13 -0
- qupled-0.0.2/qupled.egg-info/dependency_links.txt +1 -0
- qupled-0.0.2/qupled.egg-info/requires.txt +18 -0
- qupled-0.0.2/qupled.egg-info/top_level.txt +1 -0
- qupled-0.0.2/setup.cfg +4 -0
qupled-0.0.2/MANIFEST.in
ADDED
qupled-0.0.2/PKG-INFO
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: qupled
|
3
|
+
Version: 0.0.2
|
4
|
+
Summary: qupled: a package to investigate quantum plasmas via the dielectric formalism
|
5
|
+
Author-email: Federico Lucco Castello <federico.luccocastello@gmail.com>
|
6
|
+
Classifier: Programming Language :: Python :: 3.10
|
7
|
+
Classifier: Operating System :: MacOS
|
8
|
+
Classifier: Operating System :: POSIX :: Linux
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
10
|
+
Requires-Python: <3.13,>=3.10
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
Requires-Dist: matplotlib~=3.7
|
13
|
+
Requires-Dist: numpy<2.0
|
14
|
+
Requires-Dist: pandas~=2.0
|
15
|
+
Requires-Dist: tables~=3.10
|
16
|
+
Provides-Extra: testing
|
17
|
+
Requires-Dist: pytest~=8.0; extra == "testing"
|
18
|
+
Requires-Dist: pytest-mock~=3.12; extra == "testing"
|
19
|
+
Provides-Extra: docs
|
20
|
+
Requires-Dist: sphinx-rtd-theme~=2.0.0; extra == "docs"
|
21
|
+
Requires-Dist: sphinxcontrib-applehelp~=2.0.0; extra == "docs"
|
22
|
+
Requires-Dist: sphinxcontrib-devhelp~=2.0.0; extra == "docs"
|
23
|
+
Requires-Dist: sphinxcontrib-htmlhelp~=2.1.0; extra == "docs"
|
24
|
+
Requires-Dist: sphinxcontrib-jquery~=4.1; extra == "docs"
|
25
|
+
Requires-Dist: sphinxcontrib-jsmath~=1.0.1; extra == "docs"
|
26
|
+
Requires-Dist: sphinxcontrib-qthelp~=2.0.0; extra == "docs"
|
27
|
+
Requires-Dist: sphinxcontrib-serializinghtml~=2.0.0; extra == "docs"
|
@@ -0,0 +1,40 @@
|
|
1
|
+
[project]
|
2
|
+
name = "qupled"
|
3
|
+
version = "0.0.2"
|
4
|
+
description = "qupled: a package to investigate quantum plasmas via the dielectric formalism"
|
5
|
+
readme = "README.md"
|
6
|
+
requires-python = ">=3.10, <3.13"
|
7
|
+
authors = [
|
8
|
+
{ name = "Federico Lucco Castello", email = "federico.luccocastello@gmail.com" }
|
9
|
+
]
|
10
|
+
classifiers = [
|
11
|
+
"Programming Language :: Python :: 3.10",
|
12
|
+
"Operating System :: MacOS",
|
13
|
+
"Operating System :: POSIX :: Linux",
|
14
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)"
|
15
|
+
]
|
16
|
+
dependencies = [
|
17
|
+
"matplotlib~=3.7",
|
18
|
+
"numpy<2.0",
|
19
|
+
"pandas~=2.0",
|
20
|
+
"tables~=3.10"
|
21
|
+
]
|
22
|
+
|
23
|
+
[project.optional-dependencies]
|
24
|
+
testing = [
|
25
|
+
"pytest~=8.0",
|
26
|
+
"pytest-mock~=3.12"
|
27
|
+
]
|
28
|
+
docs = [
|
29
|
+
"sphinx-rtd-theme~=2.0.0",
|
30
|
+
"sphinxcontrib-applehelp~=2.0.0",
|
31
|
+
"sphinxcontrib-devhelp~=2.0.0",
|
32
|
+
"sphinxcontrib-htmlhelp~=2.1.0",
|
33
|
+
"sphinxcontrib-jquery~=4.1",
|
34
|
+
"sphinxcontrib-jsmath~=1.0.1",
|
35
|
+
"sphinxcontrib-qthelp~=2.0.0",
|
36
|
+
"sphinxcontrib-serializinghtml~=2.0.0"
|
37
|
+
]
|
38
|
+
|
39
|
+
[tool.setuptools]
|
40
|
+
include-package-data = true
|
Binary file
|
Binary file
|
@@ -0,0 +1,489 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import sys
|
3
|
+
import os
|
4
|
+
import numpy as np
|
5
|
+
import pandas as pd
|
6
|
+
from qupled import native
|
7
|
+
import qupled.util as qu
|
8
|
+
|
9
|
+
|
10
|
+
# -----------------------------------------------------------------------
|
11
|
+
# _ClassicScheme class
|
12
|
+
# -----------------------------------------------------------------------
|
13
|
+
|
14
|
+
|
15
|
+
class _ClassicScheme:
|
16
|
+
|
17
|
+
def __init__(self):
|
18
|
+
# File to store output on disk
|
19
|
+
self.hdfFileName: str = None #: Name of the output file.
|
20
|
+
|
21
|
+
# Compute the scheme
|
22
|
+
def _compute(self, scheme) -> None:
|
23
|
+
self.hdfFileName = self._getHdfFile(scheme.inputs)
|
24
|
+
status = scheme.compute()
|
25
|
+
self._checkStatusAndClean(status, scheme.recovery)
|
26
|
+
|
27
|
+
# Check that the dielectric scheme was solved without errors
|
28
|
+
@qu.MPI.runOnlyOnRoot
|
29
|
+
def _checkStatusAndClean(self, status: bool, recovery: str) -> None:
|
30
|
+
"""Checks that the scheme was solved correctly and removes temporarary files generated at run-time
|
31
|
+
|
32
|
+
Args:
|
33
|
+
status: status obtained from the native code. If status == 0 the scheme was solved correctly.
|
34
|
+
recovery: name of the recovery file.
|
35
|
+
"""
|
36
|
+
if status == 0:
|
37
|
+
if os.path.isfile(recovery):
|
38
|
+
os.remove(recovery)
|
39
|
+
print("Dielectric theory solved successfully!")
|
40
|
+
else:
|
41
|
+
sys.exit("Error while solving the dielectric theory")
|
42
|
+
|
43
|
+
# Save results to disk
|
44
|
+
def _getHdfFile(self, inputs) -> str:
|
45
|
+
"""Sets the name of the hdf file used to store the output
|
46
|
+
|
47
|
+
Args:
|
48
|
+
inputs: input parameters
|
49
|
+
"""
|
50
|
+
coupling = inputs.coupling
|
51
|
+
degeneracy = inputs.degeneracy
|
52
|
+
theory = inputs.theory
|
53
|
+
return f"rs{coupling:5.3f}_theta{degeneracy:5.3f}_{theory}.h5"
|
54
|
+
|
55
|
+
@qu.MPI.runOnlyOnRoot
|
56
|
+
def _save(self, scheme) -> None:
|
57
|
+
inputs = scheme.inputs
|
58
|
+
"""Stores the results obtained by solving the scheme."""
|
59
|
+
pd.DataFrame(
|
60
|
+
{
|
61
|
+
"coupling": inputs.coupling,
|
62
|
+
"degeneracy": inputs.degeneracy,
|
63
|
+
"theory": inputs.theory,
|
64
|
+
"resolution": inputs.resolution,
|
65
|
+
"cutoff": inputs.cutoff,
|
66
|
+
"matsubara": inputs.matsubara,
|
67
|
+
},
|
68
|
+
index=["info"],
|
69
|
+
).to_hdf(self.hdfFileName, key="info", mode="w")
|
70
|
+
pd.DataFrame(scheme.idr).to_hdf(self.hdfFileName, key="idr")
|
71
|
+
pd.DataFrame(scheme.sdr).to_hdf(self.hdfFileName, key="sdr")
|
72
|
+
pd.DataFrame(scheme.slfc).to_hdf(self.hdfFileName, key="slfc")
|
73
|
+
pd.DataFrame(scheme.ssf).to_hdf(self.hdfFileName, key="ssf")
|
74
|
+
pd.DataFrame(scheme.ssfHF).to_hdf(self.hdfFileName, key="ssfHF")
|
75
|
+
pd.DataFrame(scheme.wvg).to_hdf(self.hdfFileName, key="wvg")
|
76
|
+
|
77
|
+
# Compute radial distribution function
|
78
|
+
def computeRdf(
|
79
|
+
self, rdfGrid: np.ndarray = None, writeToHdf: bool = True
|
80
|
+
) -> np.array:
|
81
|
+
"""Computes the radial distribution function from the data stored in the output file.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
rdfGrid: The grid used to compute the radial distribution function.
|
85
|
+
Default = ``None`` (see :func:`qupled.util.Hdf.computeRdf`)
|
86
|
+
writeToHdf: Flag marking whether the rdf data should be added to the output hdf file, default to True
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
The radial distribution function
|
90
|
+
|
91
|
+
"""
|
92
|
+
if qu.MPI().getRank() > 0:
|
93
|
+
writeToHdf = False
|
94
|
+
return qu.Hdf().computeRdf(self.hdfFileName, rdfGrid, writeToHdf)
|
95
|
+
|
96
|
+
# Compute the internal energy
|
97
|
+
def computeInternalEnergy(self) -> float:
|
98
|
+
"""Computes the internal energy from the data stored in the output file.
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
The internal energy
|
102
|
+
|
103
|
+
"""
|
104
|
+
return qu.Hdf().computeInternalEnergy(self.hdfFileName)
|
105
|
+
|
106
|
+
# Plot results
|
107
|
+
@qu.MPI.runOnlyOnRoot
|
108
|
+
def plot(
|
109
|
+
self,
|
110
|
+
toPlot: list[str],
|
111
|
+
matsubara: np.ndarray = None,
|
112
|
+
rdfGrid: np.ndarray = None,
|
113
|
+
) -> None:
|
114
|
+
"""Plots the results stored in the output file.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
toPlot: A list of quantities to plot. This list can include all the values written to the
|
118
|
+
output hdf file. The radial distribution funciton is computed and added to the output
|
119
|
+
file if necessary
|
120
|
+
matsubara: A list of matsubara frequencies to plot. Applies only when the idr is plotted.
|
121
|
+
(Default = None, all matsubara frequencies are plotted)
|
122
|
+
rdfGrid: The grid used to compute the radial distribution function. Applies only when the radial
|
123
|
+
distribution function is plotted. Default = ``None`` (see :func:`qupled.util.Hdf.computeRdf`).
|
124
|
+
|
125
|
+
"""
|
126
|
+
if "rdf" in toPlot:
|
127
|
+
self.computeRdf(rdfGrid)
|
128
|
+
qu.Hdf().plot(self.hdfFileName, toPlot, matsubara)
|
129
|
+
|
130
|
+
|
131
|
+
# -----------------------------------------------------------------------
|
132
|
+
# RPA class
|
133
|
+
# -----------------------------------------------------------------------
|
134
|
+
|
135
|
+
|
136
|
+
class Rpa(_ClassicScheme):
|
137
|
+
|
138
|
+
# Compute
|
139
|
+
@qu.MPI.recordTime
|
140
|
+
@qu.MPI.synchronizeRanks
|
141
|
+
def compute(self, inputs: Rpa.Input) -> None:
|
142
|
+
"""
|
143
|
+
Solves the scheme and saves the results.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
inputs: Input parameters.
|
147
|
+
"""
|
148
|
+
scheme = native.Rpa(inputs.toNative())
|
149
|
+
self._compute(scheme)
|
150
|
+
self._save(scheme)
|
151
|
+
|
152
|
+
# Input class
|
153
|
+
class Input:
|
154
|
+
"""
|
155
|
+
Class used to manage the input for the :obj:`qupled.classic.Rpa` class.
|
156
|
+
"""
|
157
|
+
|
158
|
+
def __init__(self, coupling: float, degeneracy: float):
|
159
|
+
self.coupling: float = coupling
|
160
|
+
"""Coupling parameter."""
|
161
|
+
self.degeneracy: float = degeneracy
|
162
|
+
"""Degeneracy parameter."""
|
163
|
+
self.chemicalPotential: list[float] = [-10.0, 10.0]
|
164
|
+
"""Initial guess for the chemical potential. Default = ``[-10, 10]``"""
|
165
|
+
self.matsubara: int = 128
|
166
|
+
"""Number of Matsubara frequencies. Default = ``128``"""
|
167
|
+
self.resolution: float = 0.1
|
168
|
+
"""Resolution of the wave-vector grid. Default = ``0.1``"""
|
169
|
+
self.cutoff: float = 10.0
|
170
|
+
"""Cutoff for the wave-vector grid. Default = ``10.0``"""
|
171
|
+
self.intError: float = 1.0e-5
|
172
|
+
"""Accuracy (relative error) in the computation of integrals. Default = ``1.0e-5``"""
|
173
|
+
self.int2DScheme: str = "full"
|
174
|
+
"""
|
175
|
+
Scheme used to solve two-dimensional integrals
|
176
|
+
allowed options include:
|
177
|
+
|
178
|
+
- full: the inner integral is evaluated at arbitrary points
|
179
|
+
selected automatically by the quadrature rule
|
180
|
+
|
181
|
+
- segregated: the inner integral is evaluated on a fixed
|
182
|
+
grid that depends on the integrand that is being processed
|
183
|
+
|
184
|
+
Segregated is usually faster than full but it could become
|
185
|
+
less accurate if the fixed points are not chosen correctly. Default = ``'full'``
|
186
|
+
"""
|
187
|
+
self.threads: int = 1
|
188
|
+
"""Number of OMP threads for parallel calculations. Default = ``1``"""
|
189
|
+
self.theory: str = "RPA"
|
190
|
+
|
191
|
+
def toNative(self) -> native.RpaInput:
|
192
|
+
native_input = native.RpaInput()
|
193
|
+
for attr, value in self.__dict__.items():
|
194
|
+
setattr(native_input, attr, value)
|
195
|
+
return native_input
|
196
|
+
|
197
|
+
|
198
|
+
# -----------------------------------------------------------------------
|
199
|
+
# ESA class
|
200
|
+
# -----------------------------------------------------------------------
|
201
|
+
|
202
|
+
|
203
|
+
class ESA(_ClassicScheme):
|
204
|
+
"""
|
205
|
+
Args:
|
206
|
+
inputs: Input parameters.
|
207
|
+
"""
|
208
|
+
|
209
|
+
# Compute
|
210
|
+
@qu.MPI.recordTime
|
211
|
+
@qu.MPI.synchronizeRanks
|
212
|
+
def compute(self, inputs: ESA.Input) -> None:
|
213
|
+
"""
|
214
|
+
Solves the scheme and saves the results.
|
215
|
+
|
216
|
+
Args:
|
217
|
+
inputs: Input parameters.
|
218
|
+
"""
|
219
|
+
scheme = native.ESA(inputs.toNative())
|
220
|
+
self._compute(scheme)
|
221
|
+
self._save(scheme)
|
222
|
+
|
223
|
+
# Input class
|
224
|
+
class Input(Rpa.Input):
|
225
|
+
"""
|
226
|
+
Class used to manage the input for the :obj:`qupled.classic.ESA` class.
|
227
|
+
"""
|
228
|
+
|
229
|
+
def __init__(self, coupling: float, degeneracy: float):
|
230
|
+
super().__init__(coupling, degeneracy)
|
231
|
+
# Undocumented default values
|
232
|
+
self.theory = "ESA"
|
233
|
+
|
234
|
+
|
235
|
+
# -----------------------------------------------------------------------
|
236
|
+
# _IterativeScheme class
|
237
|
+
# -----------------------------------------------------------------------
|
238
|
+
|
239
|
+
|
240
|
+
class _IterativeScheme(_ClassicScheme):
|
241
|
+
|
242
|
+
# Set the initial guess from a dataframe produced in output
|
243
|
+
@staticmethod
|
244
|
+
def getInitialGuess(fileName: str) -> _IterativeScheme.Guess:
|
245
|
+
"""Constructs an initial guess object by extracting the information from an output file.
|
246
|
+
|
247
|
+
Args:
|
248
|
+
fileName : name of the file used to extract the information for the initial guess.
|
249
|
+
"""
|
250
|
+
hdfData = qu.Hdf().read(fileName, ["wvg", "slfc"])
|
251
|
+
return _IterativeScheme.Guess(hdfData["wvg"], hdfData["slfc"])
|
252
|
+
|
253
|
+
# Save results to disk
|
254
|
+
@qu.MPI.runOnlyOnRoot
|
255
|
+
def _save(self, scheme) -> None:
|
256
|
+
"""Stores the results obtained by solving the scheme."""
|
257
|
+
super()._save(scheme)
|
258
|
+
inputs = scheme.inputs
|
259
|
+
pd.DataFrame(
|
260
|
+
{
|
261
|
+
"coupling": inputs.coupling,
|
262
|
+
"degeneracy": inputs.degeneracy,
|
263
|
+
"error": scheme.error,
|
264
|
+
"theory": inputs.theory,
|
265
|
+
"resolution": inputs.resolution,
|
266
|
+
"cutoff": inputs.cutoff,
|
267
|
+
"matsubara": inputs.matsubara,
|
268
|
+
},
|
269
|
+
index=["info"],
|
270
|
+
).to_hdf(self.hdfFileName, key="info")
|
271
|
+
|
272
|
+
# Initial guess
|
273
|
+
class Guess:
|
274
|
+
|
275
|
+
def __init__(self, wvg: np.ndarray = None, slfc: np.ndarray = None):
|
276
|
+
self.wvg = wvg
|
277
|
+
""" Wave-vector grid. Default = ``None``"""
|
278
|
+
self.slfc = slfc
|
279
|
+
""" Static local field correction. Default = ``None``"""
|
280
|
+
|
281
|
+
def toNative(self) -> native.StlsGuess:
|
282
|
+
native_guess = native.StlsGuess()
|
283
|
+
for attr, value in self.__dict__.items():
|
284
|
+
native_value = value if value is not None else np.empty(0)
|
285
|
+
setattr(native_guess, attr, native_value)
|
286
|
+
return native_guess
|
287
|
+
|
288
|
+
|
289
|
+
# -----------------------------------------------------------------------
|
290
|
+
# Stls class
|
291
|
+
# -----------------------------------------------------------------------
|
292
|
+
|
293
|
+
|
294
|
+
class Stls(_IterativeScheme):
|
295
|
+
|
296
|
+
# Compute
|
297
|
+
@qu.MPI.recordTime
|
298
|
+
@qu.MPI.synchronizeRanks
|
299
|
+
def compute(self, inputs: Stls.Input) -> None:
|
300
|
+
"""
|
301
|
+
Solves the scheme and saves the results.
|
302
|
+
|
303
|
+
Args:
|
304
|
+
inputs: Input parameters.
|
305
|
+
"""
|
306
|
+
scheme = native.Stls(inputs.toNative())
|
307
|
+
self._compute(scheme)
|
308
|
+
self._save(scheme)
|
309
|
+
|
310
|
+
# Input class
|
311
|
+
class Input(Rpa.Input):
|
312
|
+
"""
|
313
|
+
Class used to manage the input for the :obj:`qupled.classic.Stls` class.
|
314
|
+
"""
|
315
|
+
|
316
|
+
def __init__(self, coupling: float, degeneracy: float):
|
317
|
+
super().__init__(coupling, degeneracy)
|
318
|
+
self.error: float = 1.0e-5
|
319
|
+
"""Minimum error for convergence. Default = ``1.0e-5``"""
|
320
|
+
self.mixing: float = 1.0
|
321
|
+
"""Mixing parameter. Default = ``1.0``"""
|
322
|
+
self.iterations: int = 1000
|
323
|
+
"""Maximum number of iterations. Default = ``1000``"""
|
324
|
+
self.outputFrequency: int = 10
|
325
|
+
"""Output frequency to write the recovery file. Default = ``10``"""
|
326
|
+
self.recoveryFile: str = ""
|
327
|
+
"""Name of the recovery file. Default = ``""``"""
|
328
|
+
self.guess: Stls.Guess = Stls.Guess()
|
329
|
+
"""Initial guess. Default = ``Stls.Guess()``"""
|
330
|
+
# Undocumented default values
|
331
|
+
self.theory: str = "STLS"
|
332
|
+
|
333
|
+
def toNative(self) -> native.StlsInput:
|
334
|
+
native_input = native.StlsInput()
|
335
|
+
for attr, value in self.__dict__.items():
|
336
|
+
if attr == "guess":
|
337
|
+
setattr(native_input, attr, value.toNative())
|
338
|
+
else:
|
339
|
+
setattr(native_input, attr, value)
|
340
|
+
return native_input
|
341
|
+
|
342
|
+
|
343
|
+
# -----------------------------------------------------------------------
|
344
|
+
# StlsIet class
|
345
|
+
# -----------------------------------------------------------------------
|
346
|
+
|
347
|
+
|
348
|
+
class StlsIet(_IterativeScheme):
|
349
|
+
|
350
|
+
# Compute
|
351
|
+
@qu.MPI.recordTime
|
352
|
+
@qu.MPI.synchronizeRanks
|
353
|
+
def compute(self, inputs: StlsIet.Input) -> None:
|
354
|
+
"""
|
355
|
+
Solves the scheme and saves the results.
|
356
|
+
|
357
|
+
Args:
|
358
|
+
inputs: Input parameters.
|
359
|
+
"""
|
360
|
+
scheme = native.Stls(inputs.toNative())
|
361
|
+
self._compute(scheme)
|
362
|
+
self._save(scheme)
|
363
|
+
|
364
|
+
# Save results to disk
|
365
|
+
@qu.MPI.runOnlyOnRoot
|
366
|
+
def _save(self, scheme) -> None:
|
367
|
+
"""Stores the results obtained by solving the scheme."""
|
368
|
+
super()._save(scheme)
|
369
|
+
pd.DataFrame(scheme.bf).to_hdf(self.hdfFileName, key="bf")
|
370
|
+
|
371
|
+
# Input class
|
372
|
+
class Input(Stls.Input):
|
373
|
+
"""
|
374
|
+
Class used to manage the input for the :obj:`qupled.classic.StlsIet` class.
|
375
|
+
Accepted theories: ``STLS-HNC``, ``STLS-IOI`` and ``STLS-LCT``.
|
376
|
+
"""
|
377
|
+
|
378
|
+
def __init__(self, coupling: float, degeneracy: float, theory: str):
|
379
|
+
super().__init__(coupling, degeneracy)
|
380
|
+
if theory not in {"STLS-HNC", "STLS-IOI", "STLS-LCT"}:
|
381
|
+
sys.exit("Invalid dielectric theory")
|
382
|
+
self.theory = theory
|
383
|
+
self.mapping = "standard"
|
384
|
+
r"""
|
385
|
+
Mapping for the classical-to-quantum coupling parameter
|
386
|
+
:math:`\Gamma` used in the iet schemes. Allowed options include:
|
387
|
+
|
388
|
+
- standard: :math:`\Gamma \propto \Theta^{-1}`
|
389
|
+
|
390
|
+
- sqrt: :math:`\Gamma \propto (1 + \Theta)^{-1/2}`
|
391
|
+
|
392
|
+
- linear: :math:`\Gamma \propto (1 + \Theta)^{-1}`
|
393
|
+
|
394
|
+
where :math:`\Theta` is the degeneracy parameter. Far from the ground state
|
395
|
+
(i.e. :math:`\Theta\gg1`) all mappings lead identical results, but at
|
396
|
+
the ground state they can differ significantly (the standard
|
397
|
+
mapping diverges). Default = ``standard``.
|
398
|
+
"""
|
399
|
+
|
400
|
+
def toNative(self) -> native.StlsInput:
|
401
|
+
native_input = native.StlsInput()
|
402
|
+
for attr, value in self.__dict__.items():
|
403
|
+
if attr == "guess":
|
404
|
+
setattr(native_input, attr, value.toNative())
|
405
|
+
else:
|
406
|
+
setattr(native_input, attr, value)
|
407
|
+
return native_input
|
408
|
+
|
409
|
+
|
410
|
+
# -----------------------------------------------------------------------
|
411
|
+
# VSStls class
|
412
|
+
# -----------------------------------------------------------------------
|
413
|
+
|
414
|
+
|
415
|
+
class VSStls(_IterativeScheme):
|
416
|
+
|
417
|
+
# Compute
|
418
|
+
@qu.MPI.recordTime
|
419
|
+
@qu.MPI.synchronizeRanks
|
420
|
+
def compute(self, inputs: VSStls.Input) -> None:
|
421
|
+
"""
|
422
|
+
Solves the scheme and saves the results.
|
423
|
+
|
424
|
+
Args:
|
425
|
+
inputs: Input parameters.
|
426
|
+
"""
|
427
|
+
scheme = native.VSStls(inputs.toNative())
|
428
|
+
self._compute(scheme)
|
429
|
+
self._save(scheme)
|
430
|
+
|
431
|
+
# Save results
|
432
|
+
@qu.MPI.runOnlyOnRoot
|
433
|
+
def _save(self, scheme) -> None:
|
434
|
+
"""Stores the results obtained by solving the scheme."""
|
435
|
+
super()._save(scheme)
|
436
|
+
pd.DataFrame(scheme.freeEnergyGrid).to_hdf(self.hdfFileName, key="fxcGrid")
|
437
|
+
pd.DataFrame(scheme.freeEnergyIntegrand).to_hdf(self.hdfFileName, key="fxci")
|
438
|
+
pd.DataFrame(scheme.alpha).to_hdf(self.hdfFileName, key="alpha")
|
439
|
+
|
440
|
+
# Set the free energy integrand from a dataframe produced in output
|
441
|
+
@staticmethod
|
442
|
+
def getFreeEnergyIntegrand(fileName: str) -> native.FreeEnergyIntegrand():
|
443
|
+
"""Constructs the free energy integrand by extracting the information from an output file.
|
444
|
+
|
445
|
+
Args:
|
446
|
+
fileName : name of the file used to extract the information for the free energy integrand.
|
447
|
+
"""
|
448
|
+
fxci = native.FreeEnergyIntegrand()
|
449
|
+
hdfData = qu.Hdf().read(fileName, ["fxcGrid", "fxci", "alpha"])
|
450
|
+
fxci.grid = hdfData["fxcGrid"]
|
451
|
+
fxci.integrand = np.ascontiguousarray(hdfData["fxci"])
|
452
|
+
fxci.alpha = hdfData["alpha"]
|
453
|
+
return fxci
|
454
|
+
|
455
|
+
# Input class
|
456
|
+
class Input(Stls.Input):
|
457
|
+
"""
|
458
|
+
Class used to manage the input for the :obj:`qupled.classic.VSStls` class.
|
459
|
+
"""
|
460
|
+
|
461
|
+
def __init__(self, coupling: float, degeneracy: float):
|
462
|
+
super().__init__(coupling, degeneracy)
|
463
|
+
self.alpha: list[float] = [0.5, 1.0]
|
464
|
+
"""Initial guess for the free parameter. Default = ``[0.5, 1.0]``"""
|
465
|
+
self.couplingResolution: float = 0.1
|
466
|
+
"""Resolution of the coupling parameter grid. Default = ``0.1``"""
|
467
|
+
self.degeneracyResolution: float = 0.1
|
468
|
+
"""Resolution of the degeneracy parameter grid. Default = ``0.1``"""
|
469
|
+
self.errorAlpha: float = 1.0e-3
|
470
|
+
"""Minimum error for convergence in the free parameter. Default = ``1.0e-3``"""
|
471
|
+
self.iterationsAlpha: int = 50
|
472
|
+
"""Maximum number of iterations to determine the free parameter. Default = ``50``"""
|
473
|
+
self.freeEnergyIntegrand: qupled.FreeEnergyIntegrand = (
|
474
|
+
native.FreeEnergyIntegrand()
|
475
|
+
)
|
476
|
+
"""Pre-computed free energy integrand."""
|
477
|
+
self.threads: int = 9
|
478
|
+
"""Number of threads. Default = ``9``"""
|
479
|
+
# Undocumented default values
|
480
|
+
self.theory: str = "VSSTLS"
|
481
|
+
|
482
|
+
def toNative(self) -> native.VSStlsInput:
|
483
|
+
native_input = native.VSStlsInput()
|
484
|
+
for attr, value in self.__dict__.items():
|
485
|
+
if attr == "guess":
|
486
|
+
setattr(native_input, attr, value.toNative())
|
487
|
+
else:
|
488
|
+
setattr(native_input, attr, value)
|
489
|
+
return native_input
|
@@ -0,0 +1,300 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
from shutil import rmtree
|
6
|
+
from glob import glob
|
7
|
+
import zipfile as zf
|
8
|
+
import numpy as np
|
9
|
+
import pandas as pd
|
10
|
+
from qupled import native
|
11
|
+
import qupled.util as qu
|
12
|
+
import qupled.classic as qc
|
13
|
+
|
14
|
+
# -----------------------------------------------------------------------
|
15
|
+
# _QuantumIterativeScheme class
|
16
|
+
# -----------------------------------------------------------------------
|
17
|
+
|
18
|
+
|
19
|
+
class _QuantumIterativeScheme(qc._IterativeScheme):
|
20
|
+
|
21
|
+
# Set the initial guess from a dataframe produced in output
|
22
|
+
@staticmethod
|
23
|
+
def getInitialGuess(fileName: str) -> _QuantumIterativeScheme.Guess:
|
24
|
+
"""Constructs an initial guess object by extracting the information from an output file.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
fileName : name of the file used to extract the information for the initial guess.
|
28
|
+
"""
|
29
|
+
hdfData = qu.Hdf().read(fileName, ["wvg", "ssf", "adr", "matsubara"])
|
30
|
+
return _QuantumIterativeScheme.Guess(
|
31
|
+
hdfData["wvg"],
|
32
|
+
hdfData["ssf"],
|
33
|
+
np.ascontiguousarray(hdfData["adr"]),
|
34
|
+
hdfData["matsubara"],
|
35
|
+
)
|
36
|
+
|
37
|
+
# Save results to disk
|
38
|
+
@qu.MPI.runOnlyOnRoot
|
39
|
+
def _save(self, scheme) -> None:
|
40
|
+
"""Stores the results obtained by solving the scheme."""
|
41
|
+
super()._save(scheme)
|
42
|
+
pd.DataFrame(scheme.adr).to_hdf(self.hdfFileName, key="adr")
|
43
|
+
|
44
|
+
# Initial guess
|
45
|
+
class Guess:
|
46
|
+
|
47
|
+
def __init__(
|
48
|
+
self,
|
49
|
+
wvg: np.ndarray = None,
|
50
|
+
ssf: np.ndarray = None,
|
51
|
+
adr: np.ndarray = None,
|
52
|
+
matsubara: int = 0,
|
53
|
+
):
|
54
|
+
self.wvg = wvg
|
55
|
+
""" Wave-vector grid. Default = ``None``"""
|
56
|
+
self.ssf = ssf
|
57
|
+
""" Static structure factor. Default = ``None``"""
|
58
|
+
self.adr = adr
|
59
|
+
""" Auxiliary density response. Default = ``None``"""
|
60
|
+
self.matsubara = matsubara
|
61
|
+
""" Number of matsubara frequencies. Default = ``0``"""
|
62
|
+
|
63
|
+
def toNative(self) -> native.QStlsGuess:
|
64
|
+
native_guess = native.QstlsGuess()
|
65
|
+
for attr, value in self.__dict__.items():
|
66
|
+
native_value = value if value is not None else np.empty(0)
|
67
|
+
setattr(native_guess, attr, native_value)
|
68
|
+
return native_guess
|
69
|
+
|
70
|
+
|
71
|
+
# -----------------------------------------------------------------------
|
72
|
+
# Qstls class
|
73
|
+
# -----------------------------------------------------------------------
|
74
|
+
|
75
|
+
|
76
|
+
class Qstls(_QuantumIterativeScheme):
|
77
|
+
|
78
|
+
# Compute
|
79
|
+
@qu.MPI.recordTime
|
80
|
+
@qu.MPI.synchronizeRanks
|
81
|
+
def compute(self, inputs: Qstls.Input) -> None:
|
82
|
+
"""
|
83
|
+
Solves the scheme and saves the results.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
inputs: Input parameters.
|
87
|
+
"""
|
88
|
+
scheme = native.Qstls(inputs.toNative())
|
89
|
+
self._compute(scheme)
|
90
|
+
self._save(scheme)
|
91
|
+
|
92
|
+
# Input class
|
93
|
+
class Input(qc.Stls.Input):
|
94
|
+
"""
|
95
|
+
Class used to manage the input for the :obj:`qupled.quantum.Qstls` class.
|
96
|
+
"""
|
97
|
+
|
98
|
+
def __init__(self, coupling: float, degeneracy: float):
|
99
|
+
super().__init__(coupling, degeneracy)
|
100
|
+
self.fixed: str = ""
|
101
|
+
""" Name of the file storing the fixed component of the auxiliary density
|
102
|
+
response in the QSTLS scheme. """
|
103
|
+
self.guess: Qstls.Guess = Qstls.Guess()
|
104
|
+
"""Initial guess. Default = ``Qstls.Guess()``"""
|
105
|
+
# Undocumented default values
|
106
|
+
self.theory = "QSTLS"
|
107
|
+
|
108
|
+
def toNative(self) -> native.QstlsInput:
|
109
|
+
native_input = native.QstlsInput()
|
110
|
+
for attr, value in self.__dict__.items():
|
111
|
+
if attr == "guess":
|
112
|
+
setattr(native_input, attr, value.toNative())
|
113
|
+
else:
|
114
|
+
setattr(native_input, attr, value)
|
115
|
+
return native_input
|
116
|
+
|
117
|
+
|
118
|
+
# -----------------------------------------------------------------------
|
119
|
+
# QstlsIet class
|
120
|
+
# -----------------------------------------------------------------------
|
121
|
+
|
122
|
+
|
123
|
+
class QstlsIet(_QuantumIterativeScheme):
|
124
|
+
"""
|
125
|
+
Args:
|
126
|
+
inputs: Input parameters.
|
127
|
+
"""
|
128
|
+
|
129
|
+
# Compute
|
130
|
+
@qu.MPI.recordTime
|
131
|
+
@qu.MPI.synchronizeRanks
|
132
|
+
def compute(self, inputs: QsltsIet.Input) -> None:
|
133
|
+
"""
|
134
|
+
Solves the scheme and saves the results.
|
135
|
+
|
136
|
+
Args:
|
137
|
+
inputs: Input parameters.
|
138
|
+
"""
|
139
|
+
self._unpackFixedAdrFiles(inputs)
|
140
|
+
scheme = native.Qstls(inputs.toNative())
|
141
|
+
self._compute(scheme)
|
142
|
+
self._save(scheme)
|
143
|
+
self._zipFixedAdrFiles(inputs)
|
144
|
+
self._cleanFixedAdrFiles(scheme.inputs)
|
145
|
+
|
146
|
+
# Unpack zip folder with fixed component of the auxiliary density response
|
147
|
+
@qu.MPI.runOnlyOnRoot
|
148
|
+
def _unpackFixedAdrFiles(self, inputs) -> None:
|
149
|
+
fixedIetSourceFile = inputs.fixediet
|
150
|
+
if inputs.fixediet != "":
|
151
|
+
inputs.fixediet = "qupled_tmp_run_directory"
|
152
|
+
if fixedIetSourceFile != "":
|
153
|
+
with zf.ZipFile(fixedIetSourceFile, "r") as zipFile:
|
154
|
+
zipFile.extractall(inputs.fixediet)
|
155
|
+
|
156
|
+
# Save results to disk
|
157
|
+
@qu.MPI.runOnlyOnRoot
|
158
|
+
def _save(self, scheme) -> None:
|
159
|
+
super()._save(scheme)
|
160
|
+
pd.DataFrame(scheme.bf).to_hdf(self.hdfFileName, key="bf")
|
161
|
+
|
162
|
+
# Zip all files for the fixed component of the auxiliary density response
|
163
|
+
@qu.MPI.runOnlyOnRoot
|
164
|
+
def _zipFixedAdrFiles(self, inputs) -> None:
|
165
|
+
if inputs.fixediet == "":
|
166
|
+
degeneracy = inputs.degeneracy
|
167
|
+
matsubara = inputs.matsubara
|
168
|
+
theory = inputs.theory
|
169
|
+
adrFile = f"adr_fixed_theta{degeneracy:5.3f}_matsubara{matsubara}_{theory}"
|
170
|
+
adrFileZip = f"{adrFile}.zip"
|
171
|
+
adrFileBin = f"{adrFile}_wv*.bin"
|
172
|
+
with zf.ZipFile(adrFileZip, "w") as zipFile:
|
173
|
+
for binFile in glob(adrFileBin):
|
174
|
+
zipFile.write(binFile)
|
175
|
+
os.remove(binFile)
|
176
|
+
|
177
|
+
# Remove temporaray run directory
|
178
|
+
@qu.MPI.runOnlyOnRoot
|
179
|
+
def _cleanFixedAdrFiles(self, inputs) -> None:
|
180
|
+
if os.path.isdir(inputs.fixediet):
|
181
|
+
rmtree(inputs.fixediet)
|
182
|
+
|
183
|
+
# Input class
|
184
|
+
class Input(qc.StlsIet.Input, Qstls.Input):
|
185
|
+
"""
|
186
|
+
Class used to manage the input for the :obj:`qupled.classic.QStlsIet` class.
|
187
|
+
Accepted theories: ``QSTLS-HNC``, ``QSTLS-IOI`` and ``QSTLS-LCT``.
|
188
|
+
"""
|
189
|
+
|
190
|
+
def __init__(self, coupling: float, degeneracy: float, theory: str):
|
191
|
+
qc.StlsIet.Input.__init__(self, coupling, degeneracy, "STLS-HNC")
|
192
|
+
Qstls.Input.__init__(self, coupling, degeneracy)
|
193
|
+
if theory not in {"QSTLS-HNC", "QSTLS-IOI", "QSTLS-LCT"}:
|
194
|
+
sys.exit("Invalid dielectric theory")
|
195
|
+
self.theory = theory
|
196
|
+
self.fixediet = ""
|
197
|
+
"""
|
198
|
+
Name of the zip file storing the iet part of the fixed components
|
199
|
+
of the auxiliary density response. Default = ``""``
|
200
|
+
"""
|
201
|
+
|
202
|
+
def toNative(self) -> native.QstlsInput:
|
203
|
+
native_input = native.QstlsInput()
|
204
|
+
for attr, value in self.__dict__.items():
|
205
|
+
if attr == "guess":
|
206
|
+
setattr(native_input, attr, value.toNative())
|
207
|
+
else:
|
208
|
+
setattr(native_input, attr, value)
|
209
|
+
return native_input
|
210
|
+
|
211
|
+
|
212
|
+
# -----------------------------------------------------------------------
|
213
|
+
# QVSStls class
|
214
|
+
# -----------------------------------------------------------------------
|
215
|
+
|
216
|
+
|
217
|
+
class QVSStls(_QuantumIterativeScheme):
|
218
|
+
|
219
|
+
# Compute
|
220
|
+
@qu.MPI.recordTime
|
221
|
+
@qu.MPI.synchronizeRanks
|
222
|
+
def compute(self, inputs: QVSStls.Input) -> None:
|
223
|
+
"""
|
224
|
+
Solves the scheme and saves the results.
|
225
|
+
|
226
|
+
Args:
|
227
|
+
inputs: Input parameters.
|
228
|
+
"""
|
229
|
+
self._unpackFixedAdrFiles(inputs)
|
230
|
+
scheme = native.QVSStls(inputs.toNative())
|
231
|
+
self._compute(scheme)
|
232
|
+
self._save(scheme)
|
233
|
+
self._zipFixedAdrFiles(inputs)
|
234
|
+
self._cleanFixedAdrFiles(scheme.inputs)
|
235
|
+
|
236
|
+
# Unpack zip folder with fixed component of the auxiliary density response
|
237
|
+
@qu.MPI.runOnlyOnRoot
|
238
|
+
def _unpackFixedAdrFiles(self, inputs) -> None:
|
239
|
+
fixedSourceFile = inputs.fixed
|
240
|
+
if inputs.fixed != "":
|
241
|
+
inputs.fixed = "qupled_tmp_run_directory"
|
242
|
+
if fixedSourceFile != "":
|
243
|
+
with zf.ZipFile(fixedSourceFile, "r") as zipFile:
|
244
|
+
zipFile.extractall(inputs.fixed)
|
245
|
+
|
246
|
+
# Save results to disk
|
247
|
+
@qu.MPI.runOnlyOnRoot
|
248
|
+
def _save(self, scheme) -> None:
|
249
|
+
super()._save(scheme)
|
250
|
+
pd.DataFrame(scheme.freeEnergyGrid).to_hdf(self.hdfFileName, key="fxcGrid")
|
251
|
+
pd.DataFrame(scheme.freeEnergyIntegrand).to_hdf(self.hdfFileName, key="fxci")
|
252
|
+
pd.DataFrame(scheme.alpha).to_hdf(self.hdfFileName, key="alpha")
|
253
|
+
|
254
|
+
# Zip all files for the fixed component of the auxiliary density response
|
255
|
+
@qu.MPI.runOnlyOnRoot
|
256
|
+
def _zipFixedAdrFiles(self, inputs) -> None:
|
257
|
+
if inputs.fixed == "":
|
258
|
+
degeneracy = inputs.degeneracy
|
259
|
+
matsubara = inputs.matsubara
|
260
|
+
theory = inputs.theory
|
261
|
+
adrFileZip = (
|
262
|
+
f"adr_fixed_theta{degeneracy:5.3f}_matsubara{matsubara}_{theory}.zip"
|
263
|
+
)
|
264
|
+
adrFileBin = "THETA*.bin"
|
265
|
+
with zf.ZipFile(adrFileZip, "w") as zipFile:
|
266
|
+
for binFile in glob(adrFileBin):
|
267
|
+
zipFile.write(binFile)
|
268
|
+
os.remove(binFile)
|
269
|
+
|
270
|
+
# Remove the temporary run directory
|
271
|
+
@qu.MPI.runOnlyOnRoot
|
272
|
+
def _cleanFixedAdrFiles(self, inputs) -> None:
|
273
|
+
if os.path.isdir(inputs.fixed):
|
274
|
+
rmtree(inputs.fixed)
|
275
|
+
|
276
|
+
# Set the free energy integrand from a dataframe produced in output
|
277
|
+
@staticmethod
|
278
|
+
def getFreeEnergyIntegrand(fileName: str) -> native.FreeEnergyIntegrand():
|
279
|
+
return qc.VSStls.getFreeEnergyIntegrand(fileName)
|
280
|
+
|
281
|
+
# Input class
|
282
|
+
class Input(qc.VSStls.Input, Qstls.Input):
|
283
|
+
"""
|
284
|
+
Class used to manage the input for the :obj:`qupled.classic.QVSStls` class.
|
285
|
+
"""
|
286
|
+
|
287
|
+
def __init__(self, coupling: float, degeneracy: float):
|
288
|
+
qc.VSStls.Input.__init__(self, coupling, degeneracy)
|
289
|
+
Qstls.Input.__init__(self, coupling, degeneracy)
|
290
|
+
# Undocumented default values
|
291
|
+
self.theory: str = "QVSSTLS"
|
292
|
+
|
293
|
+
def toNative(self) -> native.QVSStlsInput:
|
294
|
+
native_input = native.QVSStlsInput()
|
295
|
+
for attr, value in self.__dict__.items():
|
296
|
+
if attr == "guess":
|
297
|
+
setattr(native_input, attr, value.toNative())
|
298
|
+
else:
|
299
|
+
setattr(native_input, attr, value)
|
300
|
+
return native_input
|
@@ -0,0 +1,330 @@
|
|
1
|
+
import sys
|
2
|
+
import os
|
3
|
+
import numpy as np
|
4
|
+
import pandas as pd
|
5
|
+
import matplotlib.pyplot as plt
|
6
|
+
from matplotlib import colormaps as cm
|
7
|
+
import functools
|
8
|
+
from qupled import native
|
9
|
+
|
10
|
+
# -----------------------------------------------------------------------
|
11
|
+
# Hdf class
|
12
|
+
# -----------------------------------------------------------------------
|
13
|
+
|
14
|
+
|
15
|
+
class Hdf:
|
16
|
+
"""Class to manipulate the output hdf files produced when a scheme is solved."""
|
17
|
+
|
18
|
+
# Construct
|
19
|
+
def __init__(self):
|
20
|
+
# The first entry is a descriptive name of the
|
21
|
+
self.entries = {
|
22
|
+
"alpha": self.Entries("Free Parameter for VS schemes", "numpy"),
|
23
|
+
"adr": self.Entries("Auxiliary density response", "numpy2D"),
|
24
|
+
"bf": self.Entries("Bridge function adder", "numpy"),
|
25
|
+
"coupling": self.Entries("Coupling parameter", "number"),
|
26
|
+
"cutoff": self.Entries("Cutoff for the wave-vector grid", "number"),
|
27
|
+
"degeneracy": self.Entries("Degeneracy parameter", "number"),
|
28
|
+
"error": self.Entries("Residual error in the solution", "number"),
|
29
|
+
"fxcGrid": self.Entries("Coupling parameter", "numpy"),
|
30
|
+
"fxci": self.Entries("Free Energy integrand", "numpy2D"),
|
31
|
+
"matsubara": self.Entries("Number of matsubara frequencies", "number"),
|
32
|
+
"idr": self.Entries("Ideal density response", "numpy2D"),
|
33
|
+
"resolution": self.Entries("Resolution for the wave-vector grid", "number"),
|
34
|
+
"rdf": self.Entries("Radial distribution function", "numpy"),
|
35
|
+
"rdfGrid": self.Entries("Inter-particle distance", "numpy"),
|
36
|
+
"sdr": self.Entries("Static density response", "numpy"),
|
37
|
+
"slfc": self.Entries("Static local field correction", "numpy"),
|
38
|
+
"ssf": self.Entries("Static structure factor", "numpy"),
|
39
|
+
"ssfHF": self.Entries("Hartree-Fock static structure factor", "numpy"),
|
40
|
+
"theory": self.Entries("Theory that is being solved", "string"),
|
41
|
+
"wvg": self.Entries("Wave-vector", "numpy"),
|
42
|
+
}
|
43
|
+
|
44
|
+
# Structure used to cathegorize the entries stored in the hdf file
|
45
|
+
class Entries:
|
46
|
+
def __init__(self, description, entryType):
|
47
|
+
self.description = description # Descriptive string of the entry
|
48
|
+
self.entryType = (
|
49
|
+
entryType # Type of entry (numpy, numpy2, number or string)
|
50
|
+
)
|
51
|
+
assert (
|
52
|
+
self.entryType == "numpy"
|
53
|
+
or self.entryType == "numpy2D"
|
54
|
+
or self.entryType == "number"
|
55
|
+
or self.entryType == "string"
|
56
|
+
)
|
57
|
+
|
58
|
+
# Read data in hdf file
|
59
|
+
def read(self, hdf: str, toRead: list[str]) -> dict:
|
60
|
+
"""Reads an hdf file produced by coupled and returns the content in the form of a dictionary
|
61
|
+
|
62
|
+
Args:
|
63
|
+
hdf: Name of the hdf file to read
|
64
|
+
toRead: A list of quantities to read. The list of quantities that can be extracted from
|
65
|
+
the hdf file can be obtained by running :func:`~qupled.util.Hdf.inspect`
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
A dictionary whose entries are the quantities listed in toRead
|
69
|
+
|
70
|
+
"""
|
71
|
+
output = dict.fromkeys(toRead)
|
72
|
+
for name in toRead:
|
73
|
+
if name not in self.entries:
|
74
|
+
sys.exit("Unknown entry")
|
75
|
+
if self.entries[name].entryType == "numpy":
|
76
|
+
output[name] = pd.read_hdf(hdf, name)[0].to_numpy()
|
77
|
+
elif self.entries[name].entryType == "numpy2D":
|
78
|
+
output[name] = pd.read_hdf(hdf, name).to_numpy()
|
79
|
+
elif self.entries[name].entryType == "number":
|
80
|
+
output[name] = pd.read_hdf(hdf, "info")[name].iloc[0].tolist()
|
81
|
+
elif self.entries[name].entryType == "string":
|
82
|
+
output[name] = pd.read_hdf(hdf, "info")[name].iloc[0]
|
83
|
+
else:
|
84
|
+
sys.exit("Unknown entry type")
|
85
|
+
return output
|
86
|
+
|
87
|
+
# Get all quantities stored in an hdf file
|
88
|
+
def inspect(self, hdf: str) -> dict:
|
89
|
+
"""Allows to obtain a summary of the quantities stored in an hdf file
|
90
|
+
|
91
|
+
Args:
|
92
|
+
hdf: Name of the hdf file to inspect
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
A dictionary containing all the quantities stored in the hdf file and a brief description for
|
96
|
+
each quantity
|
97
|
+
|
98
|
+
"""
|
99
|
+
with pd.HDFStore(hdf, mode="r") as store:
|
100
|
+
datasetNames = [
|
101
|
+
name[1:] if name.startswith("/") else name for name in store.keys()
|
102
|
+
]
|
103
|
+
if "info" in datasetNames:
|
104
|
+
datasetNames.remove("info")
|
105
|
+
for name in store["info"].keys():
|
106
|
+
datasetNames.append(name)
|
107
|
+
output = dict.fromkeys(datasetNames)
|
108
|
+
for key in output.keys():
|
109
|
+
output[key] = self.entries[key].description
|
110
|
+
return output
|
111
|
+
|
112
|
+
# Plot from data in hdf file
|
113
|
+
def plot(self, hdf: str, toPlot: list[str], matsubara: np.array = None) -> None:
|
114
|
+
"""Plots the results stored in an hdf file.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
hdf: Name of the hdf file
|
118
|
+
toPlot: A list of quantities to plot. Allowed quantities include adr (auxiliary density response),
|
119
|
+
bf (bridge function adder), fxci (free energy integrand), idr (ideal density response), rdf
|
120
|
+
(radial distribution function), sdr (static density response), slfc (static local field correction)
|
121
|
+
ssf (static structure factor) and ssfHF (Hartree-Fock static structure factor).
|
122
|
+
If the hdf file does not contain the specified quantity, an error is thrown
|
123
|
+
matsubara: A list of matsubara frequencies to plot. Applies only when the idr is plotted.
|
124
|
+
(Defaults to None, all matsubara frequencies are plotted)
|
125
|
+
|
126
|
+
"""
|
127
|
+
for name in toPlot:
|
128
|
+
description = (
|
129
|
+
self.entries[name].description if name in self.entries.keys() else ""
|
130
|
+
)
|
131
|
+
if name == "rdf":
|
132
|
+
x = self.read(hdf, [name, "rdfGrid"])
|
133
|
+
Plot.plot1D(
|
134
|
+
x["rdfGrid"],
|
135
|
+
x[name],
|
136
|
+
self.entries["rdfGrid"].description,
|
137
|
+
description,
|
138
|
+
)
|
139
|
+
elif name in ["adr", "idr"]:
|
140
|
+
x = self.read(hdf, [name, "wvg", "matsubara"])
|
141
|
+
if matsubara is None:
|
142
|
+
matsubara = np.arange(x["matsubara"])
|
143
|
+
Plot.plot1DParametric(
|
144
|
+
x["wvg"],
|
145
|
+
x[name],
|
146
|
+
self.entries["wvg"].description,
|
147
|
+
description,
|
148
|
+
matsubara,
|
149
|
+
)
|
150
|
+
elif name == "fxci":
|
151
|
+
x = self.read(hdf, [name, "fxcGrid"])
|
152
|
+
Plot.plot1D(
|
153
|
+
x["fxcGrid"],
|
154
|
+
x[name][1, :],
|
155
|
+
self.entries["fxcGrid"].description,
|
156
|
+
description,
|
157
|
+
)
|
158
|
+
elif name in ["bf", "sdr", "slfc", "ssf", "ssfHF"]:
|
159
|
+
x = self.read(hdf, [name, "wvg"])
|
160
|
+
Plot.plot1D(
|
161
|
+
x["wvg"],
|
162
|
+
x[name],
|
163
|
+
self.entries["wvg"].description,
|
164
|
+
self.entries[name].description,
|
165
|
+
)
|
166
|
+
elif name == "alpha":
|
167
|
+
x = self.read(hdf, [name, "fxcGrid"])
|
168
|
+
Plot.plot1D(
|
169
|
+
x["fxcGrid"][::2],
|
170
|
+
x[name][::2],
|
171
|
+
self.entries["fxcGrid"].description,
|
172
|
+
self.entries[name].description,
|
173
|
+
)
|
174
|
+
else:
|
175
|
+
sys.exit("Unknown quantity to plot")
|
176
|
+
|
177
|
+
def computeRdf(
|
178
|
+
self, hdf: str, rdfGrid: np.array = None, saveRdf: bool = True
|
179
|
+
) -> None:
|
180
|
+
"""Computes the radial distribution function and returns it as a numpy array.
|
181
|
+
|
182
|
+
Args:
|
183
|
+
hdf: Name of the hdf file to load the structural properties from
|
184
|
+
rdfGrid: A numpy array specifing the grid used to compute the radial distribution function
|
185
|
+
(default = None, i.e. rdfGrid = np.arange(0.0, 10.0, 0.01))
|
186
|
+
saveRdf: Flag marking whether the rdf data should be added to the hdf file (default = True)
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
The radial distribution function
|
190
|
+
|
191
|
+
"""
|
192
|
+
hdfData = self.read(hdf, ["wvg", "ssf"])
|
193
|
+
if rdfGrid is None:
|
194
|
+
rdfGrid = np.arange(0.0, 10.0, 0.01)
|
195
|
+
rdf = native.computeRdf(rdfGrid, hdfData["wvg"], hdfData["ssf"])
|
196
|
+
if saveRdf:
|
197
|
+
pd.DataFrame(rdfGrid).to_hdf(hdf, key="rdfGrid", mode="r+")
|
198
|
+
pd.DataFrame(rdf).to_hdf(hdf, key="rdf", mode="r+")
|
199
|
+
return rdf
|
200
|
+
|
201
|
+
def computeInternalEnergy(self, hdf: str) -> float:
|
202
|
+
"""Computes the internal energy and returns it to output.
|
203
|
+
|
204
|
+
Args:
|
205
|
+
hdf: Name of the hdf file to load the structural properties from
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
The internal energy
|
209
|
+
"""
|
210
|
+
hdfData = self.read(hdf, ["wvg", "ssf", "coupling"])
|
211
|
+
return native.computeInternalEnergy(
|
212
|
+
hdfData["wvg"], hdfData["ssf"], hdfData["coupling"]
|
213
|
+
)
|
214
|
+
|
215
|
+
|
216
|
+
# -----------------------------------------------------------------------
|
217
|
+
# Plot class
|
218
|
+
# -----------------------------------------------------------------------
|
219
|
+
|
220
|
+
|
221
|
+
class Plot:
|
222
|
+
"""Class to collect methods used for plotting"""
|
223
|
+
|
224
|
+
# One dimensional plots
|
225
|
+
def plot1D(x, y, xlabel, ylabel):
|
226
|
+
"""Produces the plot of a one-dimensional quantity.
|
227
|
+
|
228
|
+
Positional arguments:
|
229
|
+
x -- data for the x-axis (a numpy array)
|
230
|
+
y -- data for the y-axis (a numpy array)
|
231
|
+
xlabel -- label for the x-axis (a string)
|
232
|
+
ylabel -- label for the y-axis (a string)
|
233
|
+
"""
|
234
|
+
plt.plot(x, y, "b")
|
235
|
+
plt.xlabel(xlabel)
|
236
|
+
plt.ylabel(ylabel)
|
237
|
+
plt.show()
|
238
|
+
|
239
|
+
# One dimensional plots with one parameter"
|
240
|
+
def plot1DParametric(x, y, xlabel, ylabel, parameters):
|
241
|
+
"""Produces the plot of a one-dimensional quantity that depends on an external parameter.
|
242
|
+
|
243
|
+
Positional arguments:
|
244
|
+
x -- data for the x-axis (a numpy array)
|
245
|
+
y -- data for the y-axis (a two-dimensional numpy array)
|
246
|
+
xlabel -- label for the x-axis (a string)
|
247
|
+
ylabel -- label for the y-axis (a string)
|
248
|
+
parameters -- list of parameters for which the results should be plotted
|
249
|
+
"""
|
250
|
+
numParameters = parameters.size
|
251
|
+
cmap = cm["viridis"]
|
252
|
+
for i in np.arange(numParameters):
|
253
|
+
color = cmap(1.0 * i / numParameters)
|
254
|
+
plt.plot(x, y[:, parameters[i]], color=color)
|
255
|
+
plt.xlabel(xlabel)
|
256
|
+
plt.ylabel(ylabel)
|
257
|
+
plt.show()
|
258
|
+
|
259
|
+
|
260
|
+
# -----------------------------------------------------------------------
|
261
|
+
# MPI class
|
262
|
+
# -----------------------------------------------------------------------
|
263
|
+
|
264
|
+
|
265
|
+
class MPI:
|
266
|
+
"""Class to handle the calls to the MPI API"""
|
267
|
+
|
268
|
+
def __init__(self):
|
269
|
+
self.qpMPI = native.MPI
|
270
|
+
|
271
|
+
def getRank(self):
|
272
|
+
"""Get rank of the process"""
|
273
|
+
return self.qpMPI.rank()
|
274
|
+
|
275
|
+
def isRoot(self):
|
276
|
+
"""Check if the current process is root (rank 0)"""
|
277
|
+
return self.qpMPI.isRoot()
|
278
|
+
|
279
|
+
def barrier(self):
|
280
|
+
"""Setup and MPI barrier"""
|
281
|
+
self.qpMPI.barrier()
|
282
|
+
|
283
|
+
def timer(self):
|
284
|
+
"""Get wall time"""
|
285
|
+
return self.qpMPI.timer()
|
286
|
+
|
287
|
+
@staticmethod
|
288
|
+
def runOnlyOnRoot(func):
|
289
|
+
"""Python decorator for all methods that have to be run only by root"""
|
290
|
+
|
291
|
+
@functools.wraps(func)
|
292
|
+
def wrapper(*args, **kwargs):
|
293
|
+
if MPI().isRoot():
|
294
|
+
return func(*args, **kwargs)
|
295
|
+
|
296
|
+
return wrapper
|
297
|
+
|
298
|
+
@staticmethod
|
299
|
+
def synchronizeRanks(func):
|
300
|
+
"""Python decorator for all methods that need to rank synchronization"""
|
301
|
+
|
302
|
+
@functools.wraps(func)
|
303
|
+
def wrapper(*args, **kwargs):
|
304
|
+
func(*args, **kwargs)
|
305
|
+
MPI().barrier()
|
306
|
+
|
307
|
+
return wrapper
|
308
|
+
|
309
|
+
@staticmethod
|
310
|
+
def recordTime(func):
|
311
|
+
"""Python decorator for all methods that have to be timed"""
|
312
|
+
|
313
|
+
@functools.wraps(func)
|
314
|
+
def wrapper(*args, **kwargs):
|
315
|
+
tic = MPI().timer()
|
316
|
+
func(*args, **kwargs)
|
317
|
+
toc = MPI().timer()
|
318
|
+
dt = toc - tic
|
319
|
+
hours = dt // 3600
|
320
|
+
minutes = (dt % 3600) // 60
|
321
|
+
seconds = dt % 60
|
322
|
+
if MPI().isRoot():
|
323
|
+
if hours > 0:
|
324
|
+
print("Elapsed time: %d h, %d m, %d s." % (hours, minutes, seconds))
|
325
|
+
elif minutes > 0:
|
326
|
+
print("Elapsed time: %d m, %d s." % (minutes, seconds))
|
327
|
+
else:
|
328
|
+
print("Elapsed time: %.1f s." % seconds)
|
329
|
+
|
330
|
+
return wrapper
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: qupled
|
3
|
+
Version: 0.0.2
|
4
|
+
Summary: qupled: a package to investigate quantum plasmas via the dielectric formalism
|
5
|
+
Author-email: Federico Lucco Castello <federico.luccocastello@gmail.com>
|
6
|
+
Classifier: Programming Language :: Python :: 3.10
|
7
|
+
Classifier: Operating System :: MacOS
|
8
|
+
Classifier: Operating System :: POSIX :: Linux
|
9
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
10
|
+
Requires-Python: <3.13,>=3.10
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
Requires-Dist: matplotlib~=3.7
|
13
|
+
Requires-Dist: numpy<2.0
|
14
|
+
Requires-Dist: pandas~=2.0
|
15
|
+
Requires-Dist: tables~=3.10
|
16
|
+
Provides-Extra: testing
|
17
|
+
Requires-Dist: pytest~=8.0; extra == "testing"
|
18
|
+
Requires-Dist: pytest-mock~=3.12; extra == "testing"
|
19
|
+
Provides-Extra: docs
|
20
|
+
Requires-Dist: sphinx-rtd-theme~=2.0.0; extra == "docs"
|
21
|
+
Requires-Dist: sphinxcontrib-applehelp~=2.0.0; extra == "docs"
|
22
|
+
Requires-Dist: sphinxcontrib-devhelp~=2.0.0; extra == "docs"
|
23
|
+
Requires-Dist: sphinxcontrib-htmlhelp~=2.1.0; extra == "docs"
|
24
|
+
Requires-Dist: sphinxcontrib-jquery~=4.1; extra == "docs"
|
25
|
+
Requires-Dist: sphinxcontrib-jsmath~=1.0.1; extra == "docs"
|
26
|
+
Requires-Dist: sphinxcontrib-qthelp~=2.0.0; extra == "docs"
|
27
|
+
Requires-Dist: sphinxcontrib-serializinghtml~=2.0.0; extra == "docs"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
MANIFEST.in
|
2
|
+
pyproject.toml
|
3
|
+
qupled/__init__.py
|
4
|
+
qupled/classic.py
|
5
|
+
qupled/quantum.py
|
6
|
+
qupled/util.py
|
7
|
+
qupled.egg-info/PKG-INFO
|
8
|
+
qupled.egg-info/SOURCES.txt
|
9
|
+
qupled.egg-info/dependency_links.txt
|
10
|
+
qupled.egg-info/requires.txt
|
11
|
+
qupled.egg-info/top_level.txt
|
12
|
+
qupled/Darwin/qupled.so
|
13
|
+
qupled/Linux/qupled.so
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
matplotlib~=3.7
|
2
|
+
numpy<2.0
|
3
|
+
pandas~=2.0
|
4
|
+
tables~=3.10
|
5
|
+
|
6
|
+
[docs]
|
7
|
+
sphinx-rtd-theme~=2.0.0
|
8
|
+
sphinxcontrib-applehelp~=2.0.0
|
9
|
+
sphinxcontrib-devhelp~=2.0.0
|
10
|
+
sphinxcontrib-htmlhelp~=2.1.0
|
11
|
+
sphinxcontrib-jquery~=4.1
|
12
|
+
sphinxcontrib-jsmath~=1.0.1
|
13
|
+
sphinxcontrib-qthelp~=2.0.0
|
14
|
+
sphinxcontrib-serializinghtml~=2.0.0
|
15
|
+
|
16
|
+
[testing]
|
17
|
+
pytest~=8.0
|
18
|
+
pytest-mock~=3.12
|
@@ -0,0 +1 @@
|
|
1
|
+
qupled
|
qupled-0.0.2/setup.cfg
ADDED