qupled 1.3.2__cp310-cp310-macosx_14_0_arm64.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.
qupled/hf.py ADDED
@@ -0,0 +1,263 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+
5
+ from . import database
6
+ from . import mpi
7
+ from . import native
8
+
9
+
10
+ class HF:
11
+ """
12
+ Class used to solve the HF scheme.
13
+ """
14
+
15
+ def __init__(self):
16
+ self.inputs: Input = None
17
+ """The inputs used to solve the scheme. Default = ``None``"""
18
+ self.results: Result = Result()
19
+ """The results obtained by solving the scheme"""
20
+ # Undocumented properties
21
+ self.db_handler = database.DataBaseHandler()
22
+ self.native_scheme_cls = native.HF
23
+ self.native_inputs_cls = native.Input
24
+ self.native_scheme_status = None
25
+
26
+ @property
27
+ def run_id(self):
28
+ """
29
+ Property that retrieves the run ID from the database handler.
30
+
31
+ Returns:
32
+ str: The run ID associated with the current database handler.
33
+ """
34
+ return self.db_handler.run_id
35
+
36
+ @mpi.MPI.record_time
37
+ @mpi.MPI.synchronize_ranks
38
+ def compute(self, inputs: Input):
39
+ """
40
+ Solves the scheme and saves the results.
41
+
42
+ Args:
43
+ inputs: Input parameters.
44
+ """
45
+ self.inputs = inputs
46
+ self._add_run_to_database()
47
+ self._compute_native()
48
+ self._save()
49
+
50
+ @mpi.MPI.run_only_on_root
51
+ def compute_rdf(self, rdf_grid: np.ndarray = None):
52
+ """
53
+ Computes the radial distribution function (RDF) using the provided RDF grid.
54
+ If results are available, this method computes the RDF and stores the results
55
+ in the database.
56
+
57
+ Args:
58
+ rdf_grid: A numpy array representing the RDF grid.
59
+ If not provided, a default grid will be used.
60
+ """
61
+ if self.results is not None:
62
+ self.results.compute_rdf(rdf_grid)
63
+ self.db_handler.insert_results(
64
+ {"rdf": self.results.rdf, "rdf_grid": self.results.rdf_grid}
65
+ )
66
+
67
+ def _add_run_to_database(self):
68
+ """
69
+ Adds the current run information to the database.
70
+
71
+ This method inserts the run details stored in `self.inputs` into the database
72
+ using the `db_handler`. It also updates the `database_info` attribute of
73
+ `self.inputs` with the current `run_id`.
74
+ """
75
+ self.db_handler.insert_run(self.inputs)
76
+ self.inputs.database_info.run_id = self.run_id
77
+
78
+ def _compute_native(self):
79
+ """
80
+ Computes the native representation of the inputs and processes the results.
81
+
82
+ This method performs the following steps:
83
+ 1. Converts the current inputs to their native representation.
84
+ 2. Initializes a native scheme object using the native inputs.
85
+ 3. Computes the native scheme and stores its status.
86
+ 4. Converts the results from the native scheme back to the desired format.
87
+ """
88
+ native_inputs = self.native_inputs_cls()
89
+ self.inputs.to_native(native_inputs)
90
+ scheme = self.native_scheme_cls(native_inputs)
91
+ self.native_scheme_status = scheme.compute()
92
+ self.results.from_native(scheme)
93
+
94
+ @mpi.MPI.run_only_on_root
95
+ def _save(self):
96
+ """
97
+ Saves the current state and results to the database.
98
+
99
+ This method updates the run status in the database using the current
100
+ native scheme status and inserts the results into the database.
101
+ """
102
+ self.db_handler.update_run_status(self.native_scheme_status)
103
+ self.db_handler.insert_results(self.results.__dict__)
104
+
105
+
106
+ class Input:
107
+ """
108
+ Class used to store the inputs for the :obj:`qupled.hf.HF` class.
109
+ """
110
+
111
+ def __init__(self, coupling: float, degeneracy: float):
112
+ """
113
+ Initialize the base class with the given parameters.
114
+
115
+ Parameters:
116
+ coupling (float): Coupling parameter.
117
+ degeneracy (float): Degeneracy parameter.
118
+ """
119
+ self.chemical_potential: list[float] = [-10.0, 10.0]
120
+ """Initial guess for the chemical potential. Default = ``[-10, 10]``"""
121
+ self.coupling: float = coupling
122
+ """Coupling parameter."""
123
+ self.cutoff: float = 10.0
124
+ """Cutoff for the wave-vector grid. Default = ``10.0``"""
125
+ self.degeneracy: float = degeneracy
126
+ """Degeneracy parameter."""
127
+ self.frequency_cutoff: float = 10.0
128
+ """Cutoff for the frequency (applies only in the ground state). Default = ``10.0``"""
129
+ self.integral_error: float = 1.0e-5
130
+ """Accuracy (relative error) in the computation of integrals. Default = ``1.0e-5``"""
131
+ self.integral_strategy: str = "full"
132
+ """
133
+ Scheme used to solve two-dimensional integrals
134
+ allowed options include:
135
+
136
+ - full: the inner integral is evaluated at arbitrary points selected automatically by the quadrature rule
137
+
138
+ - segregated: the inner integral is evaluated on a fixed grid that depends on the integrand that is being processed
139
+
140
+ Segregated is usually faster than full but it could become
141
+ less accurate if the fixed points are not chosen correctly. Default = ``'full'``
142
+ """
143
+ self.matsubara: int = 128
144
+ """Number of Matsubara frequencies. Default = ``128``"""
145
+ self.resolution: float = 0.1
146
+ """Resolution of the wave-vector grid. Default = ``0.1``"""
147
+ self.threads: int = 1
148
+ """Number of OMP threads for parallel calculations. Default = ``1``"""
149
+ # Undocumented default values
150
+ self.theory: str = "HF"
151
+ self.database_info: DatabaseInfo = DatabaseInfo()
152
+
153
+ def to_native(self, native_input: any):
154
+ """
155
+ Converts the attributes of the current object to their native representations
156
+ and sets them on the provided `native_input` object.
157
+
158
+ This method iterates through the attributes of the current object and checks
159
+ if the `native_input` object has a corresponding attribute. If it does, the
160
+ method attempts to convert the attribute's value to its native representation
161
+ using a `to_native` method, if available. Otherwise, it directly assigns the
162
+ attribute's value to the `native_input` object.
163
+
164
+ Args:
165
+ native_input (any): The object to which the native representations of the
166
+ current object's attributes will be assigned.
167
+ """
168
+ name = Input.to_native.__name__
169
+ for attr, value in self.__dict__.items():
170
+ if hasattr(native_input, attr) and value is not None:
171
+ value_to_set = (
172
+ tonative()
173
+ if callable(tonative := getattr(value, name, None))
174
+ else value
175
+ )
176
+ setattr(native_input, attr, value_to_set)
177
+
178
+
179
+ class Result:
180
+ """
181
+ Class used to store the results for the :obj:`qupled.hf.HF` class.
182
+ """
183
+
184
+ def __init__(self):
185
+ self.idr: np.ndarray = None
186
+ """Ideal density response"""
187
+ self.lfc: np.ndarray = None
188
+ """Local field correction"""
189
+ self.rdf: np.ndarray = None
190
+ """Radial distribution function"""
191
+ self.rdf_grid: np.ndarray = None
192
+ """Radial distribution function grid"""
193
+ self.sdr: np.ndarray = None
194
+ """Static density response"""
195
+ self.ssf: np.ndarray = None
196
+ """Static structure factor"""
197
+ self.uint: float = None
198
+ """Internal energy"""
199
+ self.wvg: np.ndarray = None
200
+ """Wave-vector grid"""
201
+
202
+ def from_native(self, native_scheme: any):
203
+ """
204
+ Updates the attributes of the current object based on the attributes of a given native scheme object.
205
+
206
+ Args:
207
+ native_scheme (any): An object containing attributes to update the current object with.
208
+
209
+ Notes:
210
+ - Only attributes that exist in both the current object and the native_scheme object will be updated.
211
+ - Attributes with a value of `None` in the native_scheme object will not overwrite the current object's attributes.
212
+ """
213
+ for attr in self.__dict__.keys():
214
+ if hasattr(native_scheme, attr):
215
+ value = getattr(native_scheme, attr)
216
+ setattr(self, attr, value) if value is not None else None
217
+
218
+ def compute_rdf(self, rdf_grid: np.ndarray | None = None):
219
+ """
220
+ Compute the radial distribution function (RDF) for the system.
221
+
222
+ Args:
223
+ rdf_grid (np.ndarray | None, optional): A 1D array specifying the grid points
224
+ at which the RDF is computed. If None, a default grid ranging from 0.0
225
+ to 10.0 with a step size of 0.01 is used.
226
+
227
+ Returns:
228
+ None: The computed RDF is stored in the `self.rdf` attribute.
229
+ """
230
+ if self.wvg is not None and self.ssf is not None:
231
+ self.rdf_grid = (
232
+ rdf_grid if rdf_grid is not None else np.arange(0.0, 10.0, 0.01)
233
+ )
234
+ self.rdf = native.compute_rdf(self.rdf_grid, self.wvg, self.ssf)
235
+
236
+
237
+ class DatabaseInfo:
238
+ """
239
+ Class used to store the database information passed to the native code.
240
+ """
241
+
242
+ def __init__(self):
243
+ self.name: str = database.DataBaseHandler.DEFAULT_DATABASE_NAME
244
+ """Database name"""
245
+ self.run_id: int = None
246
+ """ID of the run in the database"""
247
+ self.run_table_name: str = database.DataBaseHandler.RUN_TABLE_NAME
248
+ """Name of the table used to store the runs in the database"""
249
+
250
+ def to_native(self) -> native.DatabaseInfo:
251
+ """
252
+ Converts the current object to a native `DatabaseInfo` instance.
253
+ This method creates a new instance of `native.DatabaseInfo` and copies
254
+ all non-None attributes from the current object to the new instance.
255
+ Returns:
256
+ native.DatabaseInfo: A new instance of `native.DatabaseInfo` with
257
+ attributes copied from the current object.
258
+ """
259
+ native_database_info = native.DatabaseInfo()
260
+ for attr, value in self.__dict__.items():
261
+ if value is not None:
262
+ setattr(native_database_info, attr, value)
263
+ return native_database_info
qupled/mpi.py ADDED
@@ -0,0 +1,69 @@
1
+ import functools
2
+
3
+ from qupled import native
4
+
5
+
6
+ class MPI:
7
+ """Class to handle the calls to the MPI API"""
8
+
9
+ def rank(self):
10
+ """Get rank of the process"""
11
+ return native.MPI.rank()
12
+
13
+ def is_root(self):
14
+ """Check if the current process is root (rank 0)"""
15
+ return native.MPI.is_root()
16
+
17
+ def barrier(self):
18
+ """Setup an MPI barrier"""
19
+ native.MPI.barrier()
20
+
21
+ def timer(self):
22
+ """Get wall time"""
23
+ return native.MPI.timer()
24
+
25
+ @staticmethod
26
+ def run_only_on_root(func):
27
+ """Python decorator for all methods that have to be run only by root"""
28
+
29
+ @functools.wraps(func)
30
+ def wrapper(*args, **kwargs):
31
+ if native.MPI.is_root():
32
+ return func(*args, **kwargs)
33
+
34
+ return wrapper
35
+
36
+ @staticmethod
37
+ def synchronize_ranks(func):
38
+ """Python decorator for all methods that need rank synchronization"""
39
+
40
+ @functools.wraps(func)
41
+ def wrapper(*args, **kwargs):
42
+ func(*args, **kwargs)
43
+ native.MPI.barrier()
44
+
45
+ return wrapper
46
+
47
+ @staticmethod
48
+ def record_time(func):
49
+ """Python decorator for all methods that have to be timed"""
50
+
51
+ @functools.wraps(func)
52
+ def wrapper(*args, **kwargs):
53
+ mpi = native.MPI
54
+ tic = mpi.timer()
55
+ func(*args, **kwargs)
56
+ toc = mpi.timer()
57
+ dt = toc - tic
58
+ hours = dt // 3600
59
+ minutes = (dt % 3600) // 60
60
+ seconds = dt % 60
61
+ if mpi.is_root():
62
+ if hours > 0:
63
+ print("Elapsed time: %d h, %d m, %d s." % (hours, minutes, seconds))
64
+ elif minutes > 0:
65
+ print("Elapsed time: %d m, %d s." % (minutes, seconds))
66
+ else:
67
+ print("Elapsed time: %.1f s." % seconds)
68
+
69
+ return wrapper
Binary file
qupled/output.py ADDED
@@ -0,0 +1,92 @@
1
+ from . import database
2
+
3
+
4
+ class DataBase:
5
+ """Class to read and delete data from a database generated by qupled"""
6
+
7
+ # Read runs in the database
8
+ @staticmethod
9
+ def inspect_runs(database_name: str | None = None) -> dict:
10
+ """Reads runs from the database and returns the content in the form of a dictionary.
11
+
12
+ Args:
13
+ database_name: Name of the database to read from. Defaults to None.
14
+
15
+ Returns:
16
+ A dictionary whose keys are the run ids and values are the corresponding runs information.
17
+ """
18
+ db_handler = database.DataBaseHandler(database_name)
19
+ return db_handler.inspect_runs()
20
+
21
+ # Read runs in the database
22
+ @staticmethod
23
+ def read_run(
24
+ run_id: int,
25
+ database_name: str | None = None,
26
+ input_names: list[str] | None = None,
27
+ result_names: list[str] | None = None,
28
+ ) -> dict:
29
+ """
30
+ Reads a run from the database.
31
+
32
+ Args:
33
+ run_id: The ID of the run to read.
34
+ database_name: The name of the database. Defaults to None.
35
+ input_names: A list of input names to retrieve. Defaults to None.
36
+ result_names: A list of result names to retrieve. Defaults to None.
37
+
38
+ Returns:
39
+ dict: A dictionary containing the run data.
40
+ """
41
+ db_handler = database.DataBaseHandler(database_name)
42
+ return db_handler.get_run(run_id, input_names, result_names)
43
+
44
+ # Read inputs in the database
45
+ @staticmethod
46
+ def read_inputs(
47
+ run_id: int, database_name: str | None = None, names: list[str] | None = None
48
+ ) -> dict:
49
+ """Reads inputs from the database and returns the content in the form of a dictionary.
50
+
51
+ Args:
52
+ run_id: Identifier of the run to read input for.
53
+ database_name: Name of the database to read from (default is None).
54
+ names: A list of quantities to read (default is None, which reads all available quantities).
55
+
56
+ Returns:
57
+ A dictionary whose keys are the quantities listed in names and values are the corresponding inputs.
58
+ """
59
+ db_handler = database.DataBaseHandler(database_name)
60
+ return db_handler.get_inputs(run_id, names if names is not None else [])
61
+
62
+ # Read results in the database
63
+ @staticmethod
64
+ def read_results(
65
+ run_id: int, database_name: str | None = None, names: list[str] | None = None
66
+ ) -> dict:
67
+ """Reads results from the database and returns the content in the form of a dictionary.
68
+
69
+ Args:
70
+ run_id: Identifier of the run to read results for.
71
+ database_name: Name of the database to read from (default is None).
72
+ names: A list of quantities to read (default is None, which reads all available quantities).
73
+
74
+ Returns:
75
+ A dictionary whose keys are the quantities listed in names and values are the corresponding results.
76
+ """
77
+ db_handler = database.DataBaseHandler(database_name)
78
+ return db_handler.get_results(run_id, names)
79
+
80
+ # Delete results from the database
81
+ @staticmethod
82
+ def delete_run(run_id: int, database_name: str | None = None):
83
+ """
84
+ Deletes a run entry from the database based on the provided run ID.
85
+
86
+ Args:
87
+ run_id: The unique identifier of the run to be deleted.
88
+ database_name: The name of the database to connect to.
89
+ If None, the default database will be used.
90
+ """
91
+ db_handler = database.DataBaseHandler(database_name)
92
+ return db_handler.delete_run(run_id)
qupled/qstls.py ADDED
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from . import database
4
+ from . import native
5
+ from . import stls
6
+
7
+
8
+ class Qstls(stls.Stls):
9
+ """
10
+ Class used to solve the Qstls scheme.
11
+ """
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.results: stls.Result = stls.Result()
16
+ # Undocumented properties
17
+ self.native_scheme_cls = native.Qstls
18
+ self.native_inputs_cls = native.QstlsInput
19
+
20
+ def compute(self, inputs: Input):
21
+ self.find_fixed_adr_in_database(inputs)
22
+ super().compute(inputs)
23
+
24
+ def find_fixed_adr_in_database(self, inputs: Input):
25
+ """
26
+ Searches the database for a run with matching parameters and assigns its ID to the input object.
27
+
28
+ This method iterates through all runs in the database and checks if a run matches the given
29
+ input parameters (degeneracy, theory, cutoff, matsubara, and resolution). If a match is found,
30
+ the `fixed_run_id` attribute of the input object is updated with the corresponding run ID.
31
+
32
+ Args:
33
+ inputs (Input): The input parameters.
34
+
35
+ Returns:
36
+ None: The method updates the `fixed_run_id` attribute of the `inputs` object if a match is found.
37
+ """
38
+ runs = self.db_handler.inspect_runs()
39
+ inputs.fixed_run_id = None
40
+ for run in runs:
41
+ database_keys = database.DataBaseHandler.TableKeys
42
+ same_degeneracy = run[database_keys.DEGENERACY.value] == inputs.degeneracy
43
+ same_theory = run[database_keys.THEORY.value] == inputs.theory
44
+ if not same_theory or not same_degeneracy:
45
+ continue
46
+ run_id = run[database_keys.PRIMARY_KEY.value]
47
+ run_inputs = self.db_handler.get_inputs(run_id)
48
+ if (
49
+ run_inputs["cutoff"] == inputs.cutoff
50
+ and run_inputs["matsubara"] == inputs.matsubara
51
+ and run_inputs["resolution"] == inputs.resolution
52
+ ):
53
+ print(f"Loading fixed ADR from database for run_id = {run_id}")
54
+ inputs.fixed_run_id = run_id
55
+ return
56
+
57
+
58
+ # Input class
59
+ class Input(stls.Input):
60
+ """
61
+ Class used to manage the input for the :obj:`qupled.qstls.Qstls` class.
62
+ """
63
+
64
+ def __init__(self, coupling: float, degeneracy: float):
65
+ super().__init__(coupling, degeneracy)
66
+ # Undocumented default values
67
+ self.fixed_run_id: int | None = None
68
+ self.theory = "QSTLS"
qupled/qstlsiet.py ADDED
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from . import native
4
+ from . import qstls
5
+ from . import stlsiet
6
+
7
+
8
+ class QstlsIet(qstls.Qstls):
9
+ """
10
+ Class used to solve the Qstls-IET schemes.
11
+ """
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.results: stlsiet.Result = stlsiet.Result()
16
+ self.native_scheme_cls = native.QstlsIet
17
+ self.native_inputs_cls = native.QstlsIetInput
18
+
19
+ @staticmethod
20
+ def get_initial_guess(
21
+ run_id: str, database_name: str | None = None
22
+ ) -> stlsiet.Guess:
23
+ return stlsiet.StlsIet.get_initial_guess(run_id, database_name)
24
+
25
+
26
+ # Input class
27
+ class Input(stlsiet.Input, qstls.Input):
28
+ """
29
+ Class used to manage the input for the :obj:`qupled.qstlsiet.QStlsIet` class.
30
+ Accepted theories: ``QSTLS-HNC``, ``QSTLS-IOI`` and ``QSTLS-LCT``.
31
+ """
32
+
33
+ def __init__(self, coupling: float, degeneracy: float, theory: str):
34
+ super().__init__(coupling, degeneracy, "STLS-HNC")
35
+ if theory not in {"QSTLS-HNC", "QSTLS-IOI", "QSTLS-LCT"}:
36
+ raise ValueError("Invalid dielectric theory")
37
+ self.theory = theory
qupled/qvsstls.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from . import native
4
+ from . import qstls
5
+ from . import vsstls
6
+
7
+
8
+ class QVSStls(vsstls.VSStls):
9
+ """
10
+ Class used to solve the QVStls scheme.
11
+ """
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.results: vsstls.Result = vsstls.Result()
16
+ # Undocumented properties
17
+ self.native_scheme_cls = native.QVSStls
18
+ self.native_inputs_cls = native.QVSStlsInput
19
+
20
+ def compute(self, inputs: Input):
21
+ """
22
+ Solves the scheme and saves the results.
23
+
24
+ Args:
25
+ inputs: Input parameters.
26
+ """
27
+ qstls.Qstls.find_fixed_adr_in_database(self, inputs)
28
+ super().compute(inputs)
29
+
30
+ def _update_input_data(self, inputs: Input):
31
+ """
32
+ Updates the input data with additional attributes specific to the current instance.
33
+
34
+ This method overrides the parent class's `_update_input_data` method to include
35
+ logic for setting a default `fixed_run_id` if it is not already provided in the
36
+ `inputs`.
37
+
38
+ Args:
39
+ inputs: Input parameters.
40
+
41
+ Side Effects:
42
+ - If `inputs.fixed_run_id` is `None`, it is set to the value of `self.run_id`.
43
+ """
44
+ super()._update_input_data(inputs)
45
+ if inputs.fixed_run_id is None:
46
+ inputs.fixed_run_id = self.run_id
47
+
48
+
49
+ # Input class
50
+ class Input(vsstls.Input, qstls.Input):
51
+ """
52
+ Class used to manage the input for the :obj:`qupled.qvsstls.QVSStls` class.
53
+ """
54
+
55
+ def __init__(self, coupling: float, degeneracy: float):
56
+ vsstls.Input.__init__(self, coupling, degeneracy)
57
+ qstls.Input.__init__(self, coupling, degeneracy)
58
+ # Undocumented default values
59
+ self.theory: str = "QVSSTLS"
qupled/rpa.py ADDED
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from . import hf
4
+ from . import native
5
+
6
+
7
+ class Rpa(hf.HF):
8
+ """
9
+ Class used to solve the RPA scheme.
10
+ """
11
+
12
+ def __init__(self):
13
+ super().__init__()
14
+ self.results: hf.Result = hf.Result()
15
+ # Undocumented properties
16
+ self.native_scheme_cls = native.Rpa
17
+
18
+
19
+ class Input(hf.Input):
20
+ """
21
+ Class used to manage the input for the :obj:`qupled.rpa.Rpa` class.
22
+ """
23
+
24
+ def __init__(self, coupling: float, degeneracy: float):
25
+ super().__init__(coupling, degeneracy)
26
+ # Undocumented default values
27
+ self.theory = "RPA"