qupled 1.3.2__cp312-cp312-macosx_14_0_arm64.whl → 1.3.4__cp312-cp312-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.
Files changed (41) hide show
  1. qupled/.dylibs/libSQLiteCpp.0.dylib +0 -0
  2. qupled/.dylibs/{libsqlite3.3.50.1.dylib → libsqlite3.3.50.2.dylib} +0 -0
  3. qupled/database.py +66 -28
  4. qupled/esa.py +11 -5
  5. qupled/hf.py +134 -73
  6. qupled/include/fmt/args.h +235 -0
  7. qupled/include/fmt/chrono.h +2240 -0
  8. qupled/include/fmt/color.h +643 -0
  9. qupled/include/fmt/compile.h +535 -0
  10. qupled/include/fmt/core.h +2969 -0
  11. qupled/include/fmt/format-inl.h +1678 -0
  12. qupled/include/fmt/format.h +4535 -0
  13. qupled/include/fmt/os.h +455 -0
  14. qupled/include/fmt/ostream.h +245 -0
  15. qupled/include/fmt/printf.h +675 -0
  16. qupled/include/fmt/ranges.h +738 -0
  17. qupled/include/fmt/std.h +537 -0
  18. qupled/include/fmt/xchar.h +259 -0
  19. qupled/lib/cmake/fmt/fmt-config-version.cmake +43 -0
  20. qupled/lib/cmake/fmt/fmt-config.cmake +31 -0
  21. qupled/lib/cmake/fmt/fmt-targets-release.cmake +19 -0
  22. qupled/lib/cmake/fmt/fmt-targets.cmake +116 -0
  23. qupled/lib/libfmt.a +0 -0
  24. qupled/lib/pkgconfig/fmt.pc +11 -0
  25. qupled/mpi.py +104 -69
  26. qupled/native.cpython-312-darwin.so +0 -0
  27. qupled/qstls.py +13 -10
  28. qupled/qstlsiet.py +13 -10
  29. qupled/qvsstls.py +13 -11
  30. qupled/rpa.py +11 -7
  31. qupled/serialize.py +43 -0
  32. qupled/stls.py +29 -26
  33. qupled/stlsiet.py +34 -25
  34. qupled/timer.py +33 -0
  35. qupled/vsstls.py +44 -42
  36. {qupled-1.3.2.dist-info → qupled-1.3.4.dist-info}/METADATA +1 -1
  37. qupled-1.3.4.dist-info/RECORD +45 -0
  38. qupled-1.3.2.dist-info/RECORD +0 -24
  39. {qupled-1.3.2.dist-info → qupled-1.3.4.dist-info}/WHEEL +0 -0
  40. {qupled-1.3.2.dist-info → qupled-1.3.4.dist-info}/licenses/LICENSE +0 -0
  41. {qupled-1.3.2.dist-info → qupled-1.3.4.dist-info}/top_level.txt +0 -0
Binary file
qupled/database.py CHANGED
@@ -10,8 +10,6 @@ import sqlalchemy as sql
10
10
  from sqlalchemy.dialects.sqlite import insert as sqlite_insert
11
11
  import blosc2
12
12
 
13
- from . import mpi
14
-
15
13
 
16
14
  class DataBaseHandler:
17
15
  """
@@ -42,10 +40,9 @@ class DataBaseHandler:
42
40
  SUCCESS = "SUCCESS"
43
41
  FAILED = "FAILED"
44
42
 
45
- INT_TO_RUN_STATUS = {
46
- 0: RunStatus.SUCCESS,
47
- 1: RunStatus.FAILED,
48
- }
43
+ class ConflictMode(Enum):
44
+ FAIL = "FAIL"
45
+ UPDATE = "UPDATE"
49
46
 
50
47
  def __init__(self, database_name: str | None = None):
51
48
  """
@@ -76,7 +73,6 @@ class DataBaseHandler:
76
73
  self.result_table = self._build_results_table()
77
74
  self.run_id: int | None = None
78
75
 
79
- @mpi.MPI.run_only_on_root
80
76
  def insert_run(self, inputs):
81
77
  """
82
78
  Inserts a new run into the database by storing the provided inputs and results.
@@ -91,7 +87,6 @@ class DataBaseHandler:
91
87
  self._insert_run(inputs, self.RunStatus.RUNNING)
92
88
  self.insert_inputs(inputs.__dict__)
93
89
 
94
- @mpi.MPI.run_only_on_root
95
90
  def insert_inputs(self, inputs: dict[str, any]):
96
91
  """
97
92
  Inserts input data into the database for the current run.
@@ -114,8 +109,11 @@ class DataBaseHandler:
114
109
  sql_mapping = lambda value: (self._to_json(value))
115
110
  self._insert_from_dict(self.input_table, inputs, sql_mapping)
116
111
 
117
- @mpi.MPI.run_only_on_root
118
- def insert_results(self, results: dict[str, any]):
112
+ def insert_results(
113
+ self,
114
+ results: dict[str, any],
115
+ conflict_mode: ConflictMode = ConflictMode.FAIL,
116
+ ):
119
117
  """
120
118
  Inserts the given results into the database table associated with this instance.
121
119
 
@@ -130,7 +128,9 @@ class DataBaseHandler:
130
128
  """
131
129
  if self.run_id is not None:
132
130
  sql_mapping = lambda value: (self._to_bytes(value))
133
- self._insert_from_dict(self.result_table, results, sql_mapping)
131
+ self._insert_from_dict(
132
+ self.result_table, results, sql_mapping, conflict_mode
133
+ )
134
134
 
135
135
  def inspect_runs(self) -> list[dict[str, any]]:
136
136
  """
@@ -149,26 +149,27 @@ class DataBaseHandler:
149
149
  rows = self._execute(statement).mappings().all()
150
150
  return [{key: row[key] for key in row.keys()} for row in rows]
151
151
 
152
- def update_run_status(self, status: int) -> None:
152
+ def update_run_status(self, status: RunStatus) -> None:
153
153
  """
154
- Updates the status of a run in the database.
154
+ Update the status of a run in the database.
155
155
 
156
156
  Args:
157
- status (int): The new status code to update the run with. If the status
158
- code is not found in the INT_TO_RUN_STATUS mapping, the
159
- status will default to RunStatus.FAILED.
157
+ status (RunStatus): The new status to set for the run.
160
158
 
161
159
  Returns:
162
160
  None
161
+
162
+ Notes:
163
+ This method updates the status of the run identified by `self.run_id` in the run table.
164
+ If `self.run_id` is None, no update is performed.
163
165
  """
164
166
  if self.run_id is not None:
165
- new_status = self.INT_TO_RUN_STATUS.get(status, self.RunStatus.FAILED)
166
167
  statement = (
167
168
  sql.update(self.run_table)
168
169
  .where(
169
170
  self.run_table.c[self.TableKeys.PRIMARY_KEY.value] == self.run_id
170
171
  )
171
- .values({self.TableKeys.STATUS.value: new_status.value})
172
+ .values({self.TableKeys.STATUS.value: status.value})
172
173
  )
173
174
  self._execute(statement)
174
175
 
@@ -241,8 +242,6 @@ class DataBaseHandler:
241
242
  sql_mapping = lambda value: (self._from_bytes(value))
242
243
  return self._get(self.result_table, run_id, names, sql_mapping)
243
244
 
244
- @mpi.MPI.synchronize_ranks
245
- @mpi.MPI.run_only_on_root
246
245
  def delete_run(self, run_id: int) -> None:
247
246
  """
248
247
  Deletes a run entry from the database based on the provided run ID.
@@ -390,16 +389,15 @@ class DataBaseHandler:
390
389
  sql.PrimaryKeyConstraint(
391
390
  self.TableKeys.RUN_ID.value, self.TableKeys.NAME.value
392
391
  ),
392
+ sql.Index(f"idx_{table_name}_run_id", self.TableKeys.RUN_ID.value),
393
+ sql.Index(f"idx_{table_name}_name", self.TableKeys.NAME.value),
393
394
  )
394
395
  self._create_table(table)
395
396
  return table
396
397
 
397
- @mpi.MPI.synchronize_ranks
398
- @mpi.MPI.run_only_on_root
399
398
  def _create_table(self, table):
400
399
  table.create(self.engine, checkfirst=True)
401
400
 
402
- @mpi.MPI.run_only_on_root
403
401
  def _insert_run(self, inputs: any, status: RunStatus):
404
402
  """
405
403
  Inserts a new run entry into the database.
@@ -454,11 +452,15 @@ class DataBaseHandler:
454
452
  def _set_pragma(dbapi_connection, connection_record):
455
453
  cursor = dbapi_connection.cursor()
456
454
  cursor.execute("PRAGMA foreign_keys=ON")
455
+ cursor.execute("PRAGMA journal_mode=WAL")
457
456
  cursor.close()
458
457
 
459
- @mpi.MPI.run_only_on_root
460
458
  def _insert_from_dict(
461
- self, table, data: dict[str, any], sql_mapping: Callable[[any], any]
459
+ self,
460
+ table,
461
+ data: dict[str, any],
462
+ sql_mapping: Callable[[any], any],
463
+ conflict_mode: ConflictMode = ConflictMode.FAIL,
462
464
  ) -> None:
463
465
  """
464
466
  Inserts data into a specified table by mapping values through a provided SQL mapping function.
@@ -473,10 +475,46 @@ class DataBaseHandler:
473
475
  """
474
476
  for name, value in data.items():
475
477
  if mapped_value := sql_mapping(value):
476
- self._insert(table, name, mapped_value)
478
+ self._insert(table, name, mapped_value, conflict_mode)
479
+
480
+ def _insert(
481
+ self,
482
+ table: sql.Table,
483
+ name: str,
484
+ value: any,
485
+ conflict_mode: ConflictMode = ConflictMode.FAIL,
486
+ ):
487
+ """
488
+ Inserts a record into the specified SQL table with the given name and value, handling conflicts according to the specified mode.
489
+ Args:
490
+ table (sql.Table): The SQLAlchemy table object where the record will be inserted.
491
+ name (str): The name/key associated with the value to insert.
492
+ value (any): The value to be inserted into the table.
493
+ conflict_mode (ConflictMode, optional): Specifies how to handle conflicts on unique constraints.
494
+ Defaults to ConflictMode.FAIL. If set to ConflictMode.UPDATE, existing records with the same
495
+ run_id and name will be updated with the new value.
496
+ Returns:
497
+ None
498
+ Raises:
499
+ Any exceptions raised by the underlying database execution.
500
+ """
501
+ data = {
502
+ self.TableKeys.RUN_ID.value: self.run_id,
503
+ self.TableKeys.NAME.value: name,
504
+ self.TableKeys.VALUE.value: value,
505
+ }
506
+ statement = sqlite_insert(table).values(data)
507
+ if conflict_mode == self.ConflictMode.UPDATE:
508
+ statement = statement.on_conflict_do_update(
509
+ index_elements=[
510
+ self.TableKeys.RUN_ID.value,
511
+ self.TableKeys.NAME.value,
512
+ ],
513
+ set_={self.TableKeys.VALUE.value: value},
514
+ )
515
+ self._execute(statement)
477
516
 
478
- @mpi.MPI.run_only_on_root
479
- def _insert(self, table: sql.Table, name: str, value: any):
517
+ def _insert_with_update(self, table: sql.Table, name: str, value: any):
480
518
  """
481
519
  Inserts a record into the specified SQL table or updates it if a conflict occurs.
482
520
 
qupled/esa.py CHANGED
@@ -2,13 +2,17 @@ from __future__ import annotations
2
2
 
3
3
  from . import hf
4
4
  from . import native
5
+ from . import serialize
5
6
 
6
7
 
7
- class ESA(hf.HF):
8
+ class Solver(hf.Solver):
8
9
  """
9
10
  Class used to solve the ESA scheme.
10
11
  """
11
12
 
13
+ # Native classes used to solve the scheme
14
+ native_scheme_cls = native.ESA
15
+
12
16
  def __init__(self):
13
17
  super().__init__()
14
18
  self.results: hf.Result = hf.Result()
@@ -16,12 +20,14 @@ class ESA(hf.HF):
16
20
  self.native_scheme_cls = native.ESA
17
21
 
18
22
 
23
+ @serialize.serializable_dataclass
19
24
  class Input(hf.Input):
20
25
  """
21
26
  Class used to manage the input for the :obj:`qupled.esa.ESA` class.
22
27
  """
23
28
 
24
- def __init__(self, coupling: float, degeneracy: float):
25
- super().__init__(coupling, degeneracy)
26
- # Undocumented default values
27
- self.theory = "ESA"
29
+ theory: str = "ESA"
30
+
31
+
32
+ if __name__ == "__main__":
33
+ Solver.run_mpi_worker(Input, hf.Result)
qupled/hf.py CHANGED
@@ -1,17 +1,34 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
4
+
5
+ from dataclasses import field
6
+ from pathlib import Path
7
+
3
8
  import numpy as np
4
9
 
5
10
  from . import database
6
11
  from . import mpi
7
12
  from . import native
13
+ from . import serialize
14
+ from . import timer
8
15
 
9
16
 
10
- class HF:
17
+ class Solver:
11
18
  """
12
19
  Class used to solve the HF scheme.
13
20
  """
14
21
 
22
+ # Mapping of native scheme status to run status in the database
23
+ NATIVE_TO_RUN_STATUS = {
24
+ 0: database.DataBaseHandler.RunStatus.SUCCESS,
25
+ 1: database.DataBaseHandler.RunStatus.FAILED,
26
+ }
27
+
28
+ # Native classes used to solve the scheme
29
+ native_scheme_cls = native.HF
30
+ native_inputs_cls = native.Input
31
+
15
32
  def __init__(self):
16
33
  self.inputs: Input = None
17
34
  """The inputs used to solve the scheme. Default = ``None``"""
@@ -19,8 +36,6 @@ class HF:
19
36
  """The results obtained by solving the scheme"""
20
37
  # Undocumented properties
21
38
  self.db_handler = database.DataBaseHandler()
22
- self.native_scheme_cls = native.HF
23
- self.native_inputs_cls = native.Input
24
39
  self.native_scheme_status = None
25
40
 
26
41
  @property
@@ -33,8 +48,7 @@ class HF:
33
48
  """
34
49
  return self.db_handler.run_id
35
50
 
36
- @mpi.MPI.record_time
37
- @mpi.MPI.synchronize_ranks
51
+ @timer.timer
38
52
  def compute(self, inputs: Input):
39
53
  """
40
54
  Solves the scheme and saves the results.
@@ -47,7 +61,6 @@ class HF:
47
61
  self._compute_native()
48
62
  self._save()
49
63
 
50
- @mpi.MPI.run_only_on_root
51
64
  def compute_rdf(self, rdf_grid: np.ndarray = None):
52
65
  """
53
66
  Computes the radial distribution function (RDF) using the provided RDF grid.
@@ -61,7 +74,8 @@ class HF:
61
74
  if self.results is not None:
62
75
  self.results.compute_rdf(rdf_grid)
63
76
  self.db_handler.insert_results(
64
- {"rdf": self.results.rdf, "rdf_grid": self.results.rdf_grid}
77
+ {"rdf": self.results.rdf, "rdf_grid": self.results.rdf_grid},
78
+ conflict_mode=database.DataBaseHandler.ConflictMode.UPDATE,
65
79
  )
66
80
 
67
81
  def _add_run_to_database(self):
@@ -76,6 +90,19 @@ class HF:
76
90
  self.inputs.database_info.run_id = self.run_id
77
91
 
78
92
  def _compute_native(self):
93
+ """
94
+ Determines whether to execute the native computation in parallel or serial mode.
95
+
96
+ Checks if MPI (Message Passing Interface) is available and if the number of requested processes
97
+ is greater than one. If both conditions are met, runs the computation in parallel; otherwise,
98
+ runs it in serial mode.
99
+ """
100
+ if native.uses_mpi:
101
+ self._compute_native_mpi()
102
+ else:
103
+ self._compute_native_serial()
104
+
105
+ def _compute_native_serial(self):
79
106
  """
80
107
  Computes the native representation of the inputs and processes the results.
81
108
 
@@ -91,7 +118,32 @@ class HF:
91
118
  self.native_scheme_status = scheme.compute()
92
119
  self.results.from_native(scheme)
93
120
 
94
- @mpi.MPI.run_only_on_root
121
+ def _compute_native_mpi(self):
122
+ """
123
+ Executes a native MPI computation workflow.
124
+
125
+ This method performs the following steps:
126
+ 1. Writes the necessary input files for the MPI computation using `mpi.write_inputs`.
127
+ 2. Launches the MPI execution by calling `mpi.launch_mpi_execution` with the current module and the specified number of processes.
128
+ 3. Reads the computation results using `mpi.read_results` and assigns them to `self.results`.
129
+ 4. Cleans up any temporary files generated during the computation with `mpi.clean_files`.
130
+ """
131
+ mpi.write_inputs(self.inputs)
132
+ mpi.launch_mpi_execution(self.__module__, self.inputs.processes)
133
+ self.native_scheme_status = mpi.read_status()
134
+ self.results = mpi.read_results(type(self.results))
135
+ mpi.clean_files()
136
+
137
+ @classmethod
138
+ def run_mpi_worker(cls, InputCls, ResultCls):
139
+ inputs = mpi.read_inputs(InputCls)
140
+ native_inputs = cls.native_inputs_cls()
141
+ inputs.to_native(native_inputs)
142
+ scheme = cls.native_scheme_cls(native_inputs)
143
+ status = scheme.compute()
144
+ mpi.write_results(scheme, ResultCls)
145
+ mpi.write_status(scheme, status)
146
+
95
147
  def _save(self):
96
148
  """
97
149
  Saves the current state and results to the database.
@@ -99,56 +151,53 @@ class HF:
99
151
  This method updates the run status in the database using the current
100
152
  native scheme status and inserts the results into the database.
101
153
  """
102
- self.db_handler.update_run_status(self.native_scheme_status)
154
+ run_status = self.NATIVE_TO_RUN_STATUS.get(
155
+ self.native_scheme_status, database.DataBaseHandler.RunStatus.FAILED
156
+ )
157
+ self.db_handler.update_run_status(run_status)
103
158
  self.db_handler.insert_results(self.results.__dict__)
104
159
 
105
160
 
161
+ @serialize.serializable_dataclass
106
162
  class Input:
107
163
  """
108
164
  Class used to store the inputs for the :obj:`qupled.hf.HF` class.
109
165
  """
110
166
 
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:
167
+ coupling: float
168
+ """Coupling parameter."""
169
+ degeneracy: float
170
+ """Degeneracy parameter."""
171
+ chemical_potential: list[float] = field(default_factory=lambda: [-10.0, 10.0])
172
+ """Initial guess for the chemical potential. Default = ``[-10, 10]``"""
173
+ cutoff: float = 10.0
174
+ """Cutoff for the wave-vector grid. Default = ``10.0``"""
175
+ frequency_cutoff: float = 10.0
176
+ """Cutoff for the frequency (applies only in the ground state). Default = ``10.0``"""
177
+ integral_error: float = 1.0e-5
178
+ """Accuracy (relative error) in the computation of integrals. Default = ``1.0e-5``"""
179
+ integral_strategy: str = "full"
180
+ """
181
+ Scheme used to solve two-dimensional integrals
182
+ allowed options include:
135
183
 
136
- - full: the inner integral is evaluated at arbitrary points selected automatically by the quadrature rule
184
+ - full: the inner integral is evaluated at arbitrary points selected automatically by the quadrature rule
137
185
 
138
- - segregated: the inner integral is evaluated on a fixed grid that depends on the integrand that is being processed
186
+ - segregated: the inner integral is evaluated on a fixed grid that depends on the integrand that is being processed
139
187
 
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()
188
+ Segregated is usually faster than full but it could become
189
+ less accurate if the fixed points are not chosen correctly. Default = ``'full'``
190
+ """
191
+ matsubara: int = 128
192
+ """Number of Matsubara frequencies. Default = ``128``"""
193
+ resolution: float = 0.1
194
+ """Resolution of the wave-vector grid. Default = ``0.1``"""
195
+ threads: int = 1
196
+ """Number of OMP threads for parallel calculations. Default = ``1``"""
197
+ processes: int = 1
198
+ """Number of MPI processes for parallel calculations. Default = ``1``"""
199
+ theory: str = "HF"
200
+ database_info: DatabaseInfo = field(default_factory=lambda: DatabaseInfo())
152
201
 
153
202
  def to_native(self, native_input: any):
154
203
  """
@@ -176,28 +225,28 @@ class Input:
176
225
  setattr(native_input, attr, value_to_set)
177
226
 
178
227
 
228
+ @serialize.serializable_dataclass
179
229
  class Result:
180
230
  """
181
231
  Class used to store the results for the :obj:`qupled.hf.HF` class.
182
232
  """
183
233
 
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"""
234
+ idr: np.ndarray = None
235
+ """Ideal density response"""
236
+ lfc: np.ndarray = None
237
+ """Local field correction"""
238
+ rdf: np.ndarray = None
239
+ """Radial distribution function"""
240
+ rdf_grid: np.ndarray = None
241
+ """Radial distribution function grid"""
242
+ sdr: np.ndarray = None
243
+ """Static density response"""
244
+ ssf: np.ndarray = None
245
+ """Static structure factor"""
246
+ uint: float = None
247
+ """Internal energy"""
248
+ wvg: np.ndarray = None
249
+ """Wave-vector grid"""
201
250
 
202
251
  def from_native(self, native_scheme: any):
203
252
  """
@@ -210,10 +259,11 @@ class Result:
210
259
  - Only attributes that exist in both the current object and the native_scheme object will be updated.
211
260
  - Attributes with a value of `None` in the native_scheme object will not overwrite the current object's attributes.
212
261
  """
213
- for attr in self.__dict__.keys():
262
+ for attr in self.__dataclass_fields__:
214
263
  if hasattr(native_scheme, attr):
215
264
  value = getattr(native_scheme, attr)
216
- setattr(self, attr, value) if value is not None else None
265
+ valid_value = value is not None and not callable(value)
266
+ setattr(self, attr, value) if valid_value else None
217
267
 
218
268
  def compute_rdf(self, rdf_grid: np.ndarray | None = None):
219
269
  """
@@ -234,18 +284,18 @@ class Result:
234
284
  self.rdf = native.compute_rdf(self.rdf_grid, self.wvg, self.ssf)
235
285
 
236
286
 
287
+ @serialize.serializable_dataclass
237
288
  class DatabaseInfo:
238
289
  """
239
290
  Class used to store the database information passed to the native code.
240
291
  """
241
292
 
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"""
293
+ name: str = database.DataBaseHandler.DEFAULT_DATABASE_NAME
294
+ """Database name"""
295
+ run_id: int = None
296
+ """ID of the run in the database"""
297
+ run_table_name: str = database.DataBaseHandler.RUN_TABLE_NAME
298
+ """Name of the table used to store the runs in the database"""
249
299
 
250
300
  def to_native(self) -> native.DatabaseInfo:
251
301
  """
@@ -261,3 +311,14 @@ class DatabaseInfo:
261
311
  if value is not None:
262
312
  setattr(native_database_info, attr, value)
263
313
  return native_database_info
314
+
315
+ @classmethod
316
+ def from_dict(cls, d):
317
+ obj = cls.__new__(cls)
318
+ for key, value in d.items():
319
+ setattr(obj, key, value)
320
+ return obj
321
+
322
+
323
+ if __name__ == "__main__":
324
+ Solver.run_mpi_worker(Input, Result)