qrotor.system

Description

The System object contains all the information needed for a single QRotor calculation. This class can be loaded directly as qrotor.System().


  1"""
  2# Description
  3
  4The `System` object contains all the information needed for a single QRotor calculation.
  5This class can be loaded directly as `qrotor.System()`.
  6
  7---
  8"""
  9
 10
 11import numpy as np
 12from .constants import *
 13from aton import alias
 14from ._version import __version__
 15
 16
 17class System:
 18    """Quantum system.
 19
 20    Contains all the data for a single QRotor calculation, with both inputs and outputs.
 21
 22    Energy units are in meV and angles are in radians, unless stated otherwise.
 23    """
 24    def __init__(
 25            self,
 26            comment: str = None,
 27            B: float = B_CH3,
 28            gridsize: int = 200000,
 29            searched_E: int = 21,
 30            correct_potential_offset: bool = True,
 31            save_eigenvectors: bool = True,
 32            potential_name: str = '',
 33            potential_constants: list = None,
 34            tags: str = '',
 35            ):
 36        """A new quantum system can be instantiated as `system = qrotor.System()`.
 37        This new system will contain the default values listed above.
 38        """
 39        ## Technical
 40        self.version = __version__
 41        """Version of the package used to generate the data."""
 42        self.comment: str = comment
 43        """Custom comment for the dataset."""
 44        self.searched_E: int = searched_E
 45        """Number of energy eigenvalues to be searched."""
 46        self.correct_potential_offset: bool = correct_potential_offset
 47        """Correct the potential offset as `V - min(V)` or not."""
 48        self.save_eigenvectors: bool = save_eigenvectors
 49        """Save or not the eigenvectors. Final file size will be bigger."""
 50        self.tags: str = tags
 51        """Custom tags separated by spaces, such as the molecular group, etc.
 52
 53        Can be used to filter between datasets.
 54        """
 55        ## Potential
 56        self.B: float = B
 57        """Kinetic rotational energy, as in $B=\\frac{\\hbar^2}{2I}$.
 58
 59        Defaults to the value for a methyl group.
 60        """
 61        self.gridsize: int = gridsize
 62        """Number of points in the grid."""
 63        self.grid = []
 64        """The grid with the points to be used in the calculation.
 65
 66        Can be set automatically over $2 \\pi$ with `System.set_grid()`.
 67        Units must be in radians.
 68        """
 69        self.potential_name: str = potential_name
 70        """Name of the desired potential: `'zero'`, `'titov2023'`, `'test'`...
 71
 72        If empty or unrecognised, the custom potential values inside `System.potential_values` will be used. 
 73        """
 74        self.potential_constants: list = potential_constants
 75        """List of constants to be used in the calculation of the potential energy, in the `qrotor.potential` module."""
 76        self.potential_values = []
 77        """Numpy [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) with the potential values for each point in the grid.
 78
 79        Can be calculated with a function available in the `qrotor.potential` module,
 80        or loaded externally with the `qrotor.potential.load()` function.
 81        Potential energy units must be in meV.
 82        """
 83        # Potential values determined upon solving
 84        self.potential_offset: float = None
 85        """`min(V)` before offset correction when `correct_potential_offset = True`"""
 86        self.potential_min: float = None
 87        """`min(V)`"""
 88        self.potential_max: float = None
 89        """`max(V)`"""
 90        # Energies determined upon solving
 91        self.eigenvectors = []
 92        """Eigenvectors, if `save_eigenvectors` is True. Beware of the file size."""
 93        self.eigenvalues = []
 94        """Calculated eigenvalues of the system. In meV."""
 95        self.E_levels: list = []
 96        """List of `eigenvalues` grouped by energy levels, found below `potential_max`."""
 97        self.deg: float = None
 98        """Estimated degeneracy of the `E_levels` found below `potential_max`."""
 99        self.E_activation: float = None
100        """Activation energy or energy barrier, from the ground torsional state to the top of the potential barrier, `max(V) - min(eigenvalues)`. In meV."""
101        self.excitations: list = []
102        """Torsional excitations, as the difference between each energy level with respect to the ground state.
103
104        Considers the means between degenerated eigenvalues for all energy levels below `potential_max`.
105        """
106        self.splittings: list = []
107        """Tunnel splitting energies, for every degenerated energy level.
108        
109        Calculated for all `E_levels` as the difference between
110        the mean of the eigenvalues from A and the mean of the eigenvalues from E,
111        see [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.org/10.1119/1.1538575).
112        """
113        self.runtime: float = None
114        """Time taken to solve the eigenvalues."""
115
116    def solve(self, gridsize:int=None, B:int=None):
117        """Default user method to solve the quantum system.
118
119        The potential can be interpolated to a new `gridsize`.
120
121        Same as running `qrotor.solve.energies(System)`
122        with an optional new gridsize.
123        """
124        from .solve import energies
125        if gridsize:
126            self.gridsize = gridsize
127        if B:
128            self.B = B
129        return energies(self)
130
131    def solve_potential(self, gridsize:int=None):
132        """Default user method to quickly solve the potential of the quantum system.
133
134        This method does not solve the energies of the system,
135        it just computes the potential and sets `System.potential_max`,
136        `System.potential_min` and `System.potential_offset` accordingly.
137        To solve the potential AND the energies, check `System.solve()`.
138
139        The potential can be interpolated to a new `gridsize`.
140
141        Same as running `qrotor.solve.potential(System)`
142        with an optional new gridsize.
143        """
144        from .solve import potential
145        if gridsize:
146            self.gridsize = gridsize
147        return potential(self)
148
149    def change_phase(self, phase:float, calculate:bool=True):
150        """Apply a phase shift to the grid and potential values.
151
152        The `phase` should be a multiple of $\\pi$ (e.g., 3/2 for $3\\pi/2$).
153        The resulting grid will be expressed between $-2\\pi$ and $2\\pi$.
154
155        The System is solved immediately after the phase change.
156        This last step ensures that all eigenvalues and wavefunctions are correct.
157        You can override this step with `calculate = False`,
158        but remember to solve the System later!
159        """
160        if not any(self.potential_values) or not any(self.grid):
161            raise ValueError("System.potential_values and System.grid must be set before applying a phase shift.")
162        # Normalise the phase between 0 and 2
163        if abs(phase) >= 2:
164            phase = phase % 2
165        while phase < 0:
166            phase = phase + 2
167        # Shift the grid, between -2pi and 2pi
168        self.grid = (self.grid + (phase * np.pi))
169        # Apply the phase shift to potential values
170        phase_points = int((phase / 2) * self.gridsize)
171        self.potential_values = np.roll(self.potential_values, phase_points)
172        # Check that the grid is still within -2pi and 2pi, otherwise normalise it for a final time
173        while self.grid[0] <= (-2 * np.pi + 0.1):  # With a small tolerance
174            self.grid = self.grid + 2 * np.pi
175        while self.grid[-1] >= 2.5 * np.pi:  # It was not a problem until reaching 5/2 pi
176            self.grid = self.grid -2 * np.pi
177        print(f'Potential shifted by {phase}π')
178        if calculate:
179            self.solve()
180        return self
181
182    def set_grid(self, gridsize:int=None):
183        """Sets the `System.grid` to the specified `gridsize` from 0 to $2\\pi$.
184
185        If the system had a previous grid and potential values,
186        it will interpolate those values to the new gridsize,
187        using `qrotor.potential.interpolate()`.
188        """
189        if gridsize == self.gridsize:
190            return self  # Nothing to do here
191        if gridsize:
192            self.gridsize = gridsize
193        # Should we interpolate?
194        if any(self.potential_values) and any(self.grid) and self.gridsize:
195            from .potential import interpolate
196            self = interpolate(self)
197        # Should we create the values from zero?
198        elif self.gridsize:
199                self.grid = np.linspace(0, 2*np.pi, self.gridsize)
200        else:
201            raise ValueError('gridsize must be provided if there is no System.gridsize')
202        return self
203
204    def reduce_size(self):
205        """Discard data that takes too much space,
206        like eigenvectors, potential values and grids."""
207        self.eigenvectors = []
208        self.potential_values = []
209        self.grid = []
210        return self
211
212    def summary(self):
213        """Returns a dict with a summary of the System data."""
214        return {
215            'version': self.version,
216            'comment': self.comment,
217            'tags': self.tags,
218            'searched_E': self.searched_E,
219            'correct_potential_offset': self.correct_potential_offset,
220            'save_eigenvectors': self.save_eigenvectors,
221            'B': self.B,
222            'gridsize': self.gridsize,
223            'potential_name': self.potential_name,
224            'potential_constants': self.potential_constants.tolist() if isinstance(self.potential_constants, np.ndarray) else self.potential_constants,
225            'potential_offset': self.potential_offset,
226            'potential_min': self.potential_min,
227            'potential_max': self.potential_max,
228            'eigenvalues': self.eigenvalues.tolist() if isinstance(self.eigenvalues, np.ndarray) else self.eigenvalues,
229            'E_levels': self.E_levels,
230            'deg': self.deg,
231            'excitations': self.excitations,
232            'splittings': self.splittings,
233            'E_activation': self.E_activation,
234            'runtime': self.runtime,
235        }
class System:
 18class System:
 19    """Quantum system.
 20
 21    Contains all the data for a single QRotor calculation, with both inputs and outputs.
 22
 23    Energy units are in meV and angles are in radians, unless stated otherwise.
 24    """
 25    def __init__(
 26            self,
 27            comment: str = None,
 28            B: float = B_CH3,
 29            gridsize: int = 200000,
 30            searched_E: int = 21,
 31            correct_potential_offset: bool = True,
 32            save_eigenvectors: bool = True,
 33            potential_name: str = '',
 34            potential_constants: list = None,
 35            tags: str = '',
 36            ):
 37        """A new quantum system can be instantiated as `system = qrotor.System()`.
 38        This new system will contain the default values listed above.
 39        """
 40        ## Technical
 41        self.version = __version__
 42        """Version of the package used to generate the data."""
 43        self.comment: str = comment
 44        """Custom comment for the dataset."""
 45        self.searched_E: int = searched_E
 46        """Number of energy eigenvalues to be searched."""
 47        self.correct_potential_offset: bool = correct_potential_offset
 48        """Correct the potential offset as `V - min(V)` or not."""
 49        self.save_eigenvectors: bool = save_eigenvectors
 50        """Save or not the eigenvectors. Final file size will be bigger."""
 51        self.tags: str = tags
 52        """Custom tags separated by spaces, such as the molecular group, etc.
 53
 54        Can be used to filter between datasets.
 55        """
 56        ## Potential
 57        self.B: float = B
 58        """Kinetic rotational energy, as in $B=\\frac{\\hbar^2}{2I}$.
 59
 60        Defaults to the value for a methyl group.
 61        """
 62        self.gridsize: int = gridsize
 63        """Number of points in the grid."""
 64        self.grid = []
 65        """The grid with the points to be used in the calculation.
 66
 67        Can be set automatically over $2 \\pi$ with `System.set_grid()`.
 68        Units must be in radians.
 69        """
 70        self.potential_name: str = potential_name
 71        """Name of the desired potential: `'zero'`, `'titov2023'`, `'test'`...
 72
 73        If empty or unrecognised, the custom potential values inside `System.potential_values` will be used. 
 74        """
 75        self.potential_constants: list = potential_constants
 76        """List of constants to be used in the calculation of the potential energy, in the `qrotor.potential` module."""
 77        self.potential_values = []
 78        """Numpy [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) with the potential values for each point in the grid.
 79
 80        Can be calculated with a function available in the `qrotor.potential` module,
 81        or loaded externally with the `qrotor.potential.load()` function.
 82        Potential energy units must be in meV.
 83        """
 84        # Potential values determined upon solving
 85        self.potential_offset: float = None
 86        """`min(V)` before offset correction when `correct_potential_offset = True`"""
 87        self.potential_min: float = None
 88        """`min(V)`"""
 89        self.potential_max: float = None
 90        """`max(V)`"""
 91        # Energies determined upon solving
 92        self.eigenvectors = []
 93        """Eigenvectors, if `save_eigenvectors` is True. Beware of the file size."""
 94        self.eigenvalues = []
 95        """Calculated eigenvalues of the system. In meV."""
 96        self.E_levels: list = []
 97        """List of `eigenvalues` grouped by energy levels, found below `potential_max`."""
 98        self.deg: float = None
 99        """Estimated degeneracy of the `E_levels` found below `potential_max`."""
100        self.E_activation: float = None
101        """Activation energy or energy barrier, from the ground torsional state to the top of the potential barrier, `max(V) - min(eigenvalues)`. In meV."""
102        self.excitations: list = []
103        """Torsional excitations, as the difference between each energy level with respect to the ground state.
104
105        Considers the means between degenerated eigenvalues for all energy levels below `potential_max`.
106        """
107        self.splittings: list = []
108        """Tunnel splitting energies, for every degenerated energy level.
109        
110        Calculated for all `E_levels` as the difference between
111        the mean of the eigenvalues from A and the mean of the eigenvalues from E,
112        see [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.org/10.1119/1.1538575).
113        """
114        self.runtime: float = None
115        """Time taken to solve the eigenvalues."""
116
117    def solve(self, gridsize:int=None, B:int=None):
118        """Default user method to solve the quantum system.
119
120        The potential can be interpolated to a new `gridsize`.
121
122        Same as running `qrotor.solve.energies(System)`
123        with an optional new gridsize.
124        """
125        from .solve import energies
126        if gridsize:
127            self.gridsize = gridsize
128        if B:
129            self.B = B
130        return energies(self)
131
132    def solve_potential(self, gridsize:int=None):
133        """Default user method to quickly solve the potential of the quantum system.
134
135        This method does not solve the energies of the system,
136        it just computes the potential and sets `System.potential_max`,
137        `System.potential_min` and `System.potential_offset` accordingly.
138        To solve the potential AND the energies, check `System.solve()`.
139
140        The potential can be interpolated to a new `gridsize`.
141
142        Same as running `qrotor.solve.potential(System)`
143        with an optional new gridsize.
144        """
145        from .solve import potential
146        if gridsize:
147            self.gridsize = gridsize
148        return potential(self)
149
150    def change_phase(self, phase:float, calculate:bool=True):
151        """Apply a phase shift to the grid and potential values.
152
153        The `phase` should be a multiple of $\\pi$ (e.g., 3/2 for $3\\pi/2$).
154        The resulting grid will be expressed between $-2\\pi$ and $2\\pi$.
155
156        The System is solved immediately after the phase change.
157        This last step ensures that all eigenvalues and wavefunctions are correct.
158        You can override this step with `calculate = False`,
159        but remember to solve the System later!
160        """
161        if not any(self.potential_values) or not any(self.grid):
162            raise ValueError("System.potential_values and System.grid must be set before applying a phase shift.")
163        # Normalise the phase between 0 and 2
164        if abs(phase) >= 2:
165            phase = phase % 2
166        while phase < 0:
167            phase = phase + 2
168        # Shift the grid, between -2pi and 2pi
169        self.grid = (self.grid + (phase * np.pi))
170        # Apply the phase shift to potential values
171        phase_points = int((phase / 2) * self.gridsize)
172        self.potential_values = np.roll(self.potential_values, phase_points)
173        # Check that the grid is still within -2pi and 2pi, otherwise normalise it for a final time
174        while self.grid[0] <= (-2 * np.pi + 0.1):  # With a small tolerance
175            self.grid = self.grid + 2 * np.pi
176        while self.grid[-1] >= 2.5 * np.pi:  # It was not a problem until reaching 5/2 pi
177            self.grid = self.grid -2 * np.pi
178        print(f'Potential shifted by {phase}π')
179        if calculate:
180            self.solve()
181        return self
182
183    def set_grid(self, gridsize:int=None):
184        """Sets the `System.grid` to the specified `gridsize` from 0 to $2\\pi$.
185
186        If the system had a previous grid and potential values,
187        it will interpolate those values to the new gridsize,
188        using `qrotor.potential.interpolate()`.
189        """
190        if gridsize == self.gridsize:
191            return self  # Nothing to do here
192        if gridsize:
193            self.gridsize = gridsize
194        # Should we interpolate?
195        if any(self.potential_values) and any(self.grid) and self.gridsize:
196            from .potential import interpolate
197            self = interpolate(self)
198        # Should we create the values from zero?
199        elif self.gridsize:
200                self.grid = np.linspace(0, 2*np.pi, self.gridsize)
201        else:
202            raise ValueError('gridsize must be provided if there is no System.gridsize')
203        return self
204
205    def reduce_size(self):
206        """Discard data that takes too much space,
207        like eigenvectors, potential values and grids."""
208        self.eigenvectors = []
209        self.potential_values = []
210        self.grid = []
211        return self
212
213    def summary(self):
214        """Returns a dict with a summary of the System data."""
215        return {
216            'version': self.version,
217            'comment': self.comment,
218            'tags': self.tags,
219            'searched_E': self.searched_E,
220            'correct_potential_offset': self.correct_potential_offset,
221            'save_eigenvectors': self.save_eigenvectors,
222            'B': self.B,
223            'gridsize': self.gridsize,
224            'potential_name': self.potential_name,
225            'potential_constants': self.potential_constants.tolist() if isinstance(self.potential_constants, np.ndarray) else self.potential_constants,
226            'potential_offset': self.potential_offset,
227            'potential_min': self.potential_min,
228            'potential_max': self.potential_max,
229            'eigenvalues': self.eigenvalues.tolist() if isinstance(self.eigenvalues, np.ndarray) else self.eigenvalues,
230            'E_levels': self.E_levels,
231            'deg': self.deg,
232            'excitations': self.excitations,
233            'splittings': self.splittings,
234            'E_activation': self.E_activation,
235            'runtime': self.runtime,
236        }

Quantum system.

Contains all the data for a single QRotor calculation, with both inputs and outputs.

Energy units are in meV and angles are in radians, unless stated otherwise.

System( comment: str = None, B: float = np.float64(0.6451810934439263), gridsize: int = 200000, searched_E: int = 21, correct_potential_offset: bool = True, save_eigenvectors: bool = True, potential_name: str = '', potential_constants: list = None, tags: str = '')
 25    def __init__(
 26            self,
 27            comment: str = None,
 28            B: float = B_CH3,
 29            gridsize: int = 200000,
 30            searched_E: int = 21,
 31            correct_potential_offset: bool = True,
 32            save_eigenvectors: bool = True,
 33            potential_name: str = '',
 34            potential_constants: list = None,
 35            tags: str = '',
 36            ):
 37        """A new quantum system can be instantiated as `system = qrotor.System()`.
 38        This new system will contain the default values listed above.
 39        """
 40        ## Technical
 41        self.version = __version__
 42        """Version of the package used to generate the data."""
 43        self.comment: str = comment
 44        """Custom comment for the dataset."""
 45        self.searched_E: int = searched_E
 46        """Number of energy eigenvalues to be searched."""
 47        self.correct_potential_offset: bool = correct_potential_offset
 48        """Correct the potential offset as `V - min(V)` or not."""
 49        self.save_eigenvectors: bool = save_eigenvectors
 50        """Save or not the eigenvectors. Final file size will be bigger."""
 51        self.tags: str = tags
 52        """Custom tags separated by spaces, such as the molecular group, etc.
 53
 54        Can be used to filter between datasets.
 55        """
 56        ## Potential
 57        self.B: float = B
 58        """Kinetic rotational energy, as in $B=\\frac{\\hbar^2}{2I}$.
 59
 60        Defaults to the value for a methyl group.
 61        """
 62        self.gridsize: int = gridsize
 63        """Number of points in the grid."""
 64        self.grid = []
 65        """The grid with the points to be used in the calculation.
 66
 67        Can be set automatically over $2 \\pi$ with `System.set_grid()`.
 68        Units must be in radians.
 69        """
 70        self.potential_name: str = potential_name
 71        """Name of the desired potential: `'zero'`, `'titov2023'`, `'test'`...
 72
 73        If empty or unrecognised, the custom potential values inside `System.potential_values` will be used. 
 74        """
 75        self.potential_constants: list = potential_constants
 76        """List of constants to be used in the calculation of the potential energy, in the `qrotor.potential` module."""
 77        self.potential_values = []
 78        """Numpy [ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) with the potential values for each point in the grid.
 79
 80        Can be calculated with a function available in the `qrotor.potential` module,
 81        or loaded externally with the `qrotor.potential.load()` function.
 82        Potential energy units must be in meV.
 83        """
 84        # Potential values determined upon solving
 85        self.potential_offset: float = None
 86        """`min(V)` before offset correction when `correct_potential_offset = True`"""
 87        self.potential_min: float = None
 88        """`min(V)`"""
 89        self.potential_max: float = None
 90        """`max(V)`"""
 91        # Energies determined upon solving
 92        self.eigenvectors = []
 93        """Eigenvectors, if `save_eigenvectors` is True. Beware of the file size."""
 94        self.eigenvalues = []
 95        """Calculated eigenvalues of the system. In meV."""
 96        self.E_levels: list = []
 97        """List of `eigenvalues` grouped by energy levels, found below `potential_max`."""
 98        self.deg: float = None
 99        """Estimated degeneracy of the `E_levels` found below `potential_max`."""
100        self.E_activation: float = None
101        """Activation energy or energy barrier, from the ground torsional state to the top of the potential barrier, `max(V) - min(eigenvalues)`. In meV."""
102        self.excitations: list = []
103        """Torsional excitations, as the difference between each energy level with respect to the ground state.
104
105        Considers the means between degenerated eigenvalues for all energy levels below `potential_max`.
106        """
107        self.splittings: list = []
108        """Tunnel splitting energies, for every degenerated energy level.
109        
110        Calculated for all `E_levels` as the difference between
111        the mean of the eigenvalues from A and the mean of the eigenvalues from E,
112        see [R. M. Dimeo, American Journal of Physics 71, 885–893 (2003)](https://doi.org/10.1119/1.1538575).
113        """
114        self.runtime: float = None
115        """Time taken to solve the eigenvalues."""

A new quantum system can be instantiated as system = qrotor.System(). This new system will contain the default values listed above.

version

Version of the package used to generate the data.

comment: str

Custom comment for the dataset.

searched_E: int

Number of energy eigenvalues to be searched.

correct_potential_offset: bool

Correct the potential offset as V - min(V) or not.

save_eigenvectors: bool

Save or not the eigenvectors. Final file size will be bigger.

tags: str

Custom tags separated by spaces, such as the molecular group, etc.

Can be used to filter between datasets.

B: float

Kinetic rotational energy, as in $B=\frac{\hbar^2}{2I}$.

Defaults to the value for a methyl group.

gridsize: int

Number of points in the grid.

grid

The grid with the points to be used in the calculation.

Can be set automatically over $2 \pi$ with System.set_grid(). Units must be in radians.

potential_name: str

Name of the desired potential: 'zero', 'titov2023', 'test'...

If empty or unrecognised, the custom potential values inside System.potential_values will be used.

potential_constants: list

List of constants to be used in the calculation of the potential energy, in the qrotor.potential module.

potential_values

Numpy ndarray with the potential values for each point in the grid.

Can be calculated with a function available in the qrotor.potential module, or loaded externally with the qrotor.potential.load() function. Potential energy units must be in meV.

potential_offset: float

min(V) before offset correction when correct_potential_offset = True

potential_min: float

min(V)

potential_max: float

max(V)

eigenvectors

Eigenvectors, if save_eigenvectors is True. Beware of the file size.

eigenvalues

Calculated eigenvalues of the system. In meV.

E_levels: list

List of eigenvalues grouped by energy levels, found below potential_max.

deg: float

Estimated degeneracy of the E_levels found below potential_max.

E_activation: float

Activation energy or energy barrier, from the ground torsional state to the top of the potential barrier, max(V) - min(eigenvalues). In meV.

excitations: list

Torsional excitations, as the difference between each energy level with respect to the ground state.

Considers the means between degenerated eigenvalues for all energy levels below potential_max.

splittings: list

Tunnel splitting energies, for every degenerated energy level.

Calculated for all E_levels as the difference between the mean of the eigenvalues from A and the mean of the eigenvalues from E, see R. M. Dimeo, American Journal of Physics 71, 885–893 (2003).

runtime: float

Time taken to solve the eigenvalues.

def solve(self, gridsize: int = None, B: int = None):
117    def solve(self, gridsize:int=None, B:int=None):
118        """Default user method to solve the quantum system.
119
120        The potential can be interpolated to a new `gridsize`.
121
122        Same as running `qrotor.solve.energies(System)`
123        with an optional new gridsize.
124        """
125        from .solve import energies
126        if gridsize:
127            self.gridsize = gridsize
128        if B:
129            self.B = B
130        return energies(self)

Default user method to solve the quantum system.

The potential can be interpolated to a new gridsize.

Same as running qrotor.solve.energies(System) with an optional new gridsize.

def solve_potential(self, gridsize: int = None):
132    def solve_potential(self, gridsize:int=None):
133        """Default user method to quickly solve the potential of the quantum system.
134
135        This method does not solve the energies of the system,
136        it just computes the potential and sets `System.potential_max`,
137        `System.potential_min` and `System.potential_offset` accordingly.
138        To solve the potential AND the energies, check `System.solve()`.
139
140        The potential can be interpolated to a new `gridsize`.
141
142        Same as running `qrotor.solve.potential(System)`
143        with an optional new gridsize.
144        """
145        from .solve import potential
146        if gridsize:
147            self.gridsize = gridsize
148        return potential(self)

Default user method to quickly solve the potential of the quantum system.

This method does not solve the energies of the system, it just computes the potential and sets System.potential_max, System.potential_min and System.potential_offset accordingly. To solve the potential AND the energies, check System.solve().

The potential can be interpolated to a new gridsize.

Same as running qrotor.solve.potential(System) with an optional new gridsize.

def change_phase(self, phase: float, calculate: bool = True):
150    def change_phase(self, phase:float, calculate:bool=True):
151        """Apply a phase shift to the grid and potential values.
152
153        The `phase` should be a multiple of $\\pi$ (e.g., 3/2 for $3\\pi/2$).
154        The resulting grid will be expressed between $-2\\pi$ and $2\\pi$.
155
156        The System is solved immediately after the phase change.
157        This last step ensures that all eigenvalues and wavefunctions are correct.
158        You can override this step with `calculate = False`,
159        but remember to solve the System later!
160        """
161        if not any(self.potential_values) or not any(self.grid):
162            raise ValueError("System.potential_values and System.grid must be set before applying a phase shift.")
163        # Normalise the phase between 0 and 2
164        if abs(phase) >= 2:
165            phase = phase % 2
166        while phase < 0:
167            phase = phase + 2
168        # Shift the grid, between -2pi and 2pi
169        self.grid = (self.grid + (phase * np.pi))
170        # Apply the phase shift to potential values
171        phase_points = int((phase / 2) * self.gridsize)
172        self.potential_values = np.roll(self.potential_values, phase_points)
173        # Check that the grid is still within -2pi and 2pi, otherwise normalise it for a final time
174        while self.grid[0] <= (-2 * np.pi + 0.1):  # With a small tolerance
175            self.grid = self.grid + 2 * np.pi
176        while self.grid[-1] >= 2.5 * np.pi:  # It was not a problem until reaching 5/2 pi
177            self.grid = self.grid -2 * np.pi
178        print(f'Potential shifted by {phase}π')
179        if calculate:
180            self.solve()
181        return self

Apply a phase shift to the grid and potential values.

The phase should be a multiple of $\pi$ (e.g., 3/2 for $3\pi/2$). The resulting grid will be expressed between $-2\pi$ and $2\pi$.

The System is solved immediately after the phase change. This last step ensures that all eigenvalues and wavefunctions are correct. You can override this step with calculate = False, but remember to solve the System later!

def set_grid(self, gridsize: int = None):
183    def set_grid(self, gridsize:int=None):
184        """Sets the `System.grid` to the specified `gridsize` from 0 to $2\\pi$.
185
186        If the system had a previous grid and potential values,
187        it will interpolate those values to the new gridsize,
188        using `qrotor.potential.interpolate()`.
189        """
190        if gridsize == self.gridsize:
191            return self  # Nothing to do here
192        if gridsize:
193            self.gridsize = gridsize
194        # Should we interpolate?
195        if any(self.potential_values) and any(self.grid) and self.gridsize:
196            from .potential import interpolate
197            self = interpolate(self)
198        # Should we create the values from zero?
199        elif self.gridsize:
200                self.grid = np.linspace(0, 2*np.pi, self.gridsize)
201        else:
202            raise ValueError('gridsize must be provided if there is no System.gridsize')
203        return self

Sets the System.grid to the specified gridsize from 0 to $2\pi$.

If the system had a previous grid and potential values, it will interpolate those values to the new gridsize, using qrotor.potential.interpolate().

def reduce_size(self):
205    def reduce_size(self):
206        """Discard data that takes too much space,
207        like eigenvectors, potential values and grids."""
208        self.eigenvectors = []
209        self.potential_values = []
210        self.grid = []
211        return self

Discard data that takes too much space, like eigenvectors, potential values and grids.

def summary(self):
213    def summary(self):
214        """Returns a dict with a summary of the System data."""
215        return {
216            'version': self.version,
217            'comment': self.comment,
218            'tags': self.tags,
219            'searched_E': self.searched_E,
220            'correct_potential_offset': self.correct_potential_offset,
221            'save_eigenvectors': self.save_eigenvectors,
222            'B': self.B,
223            'gridsize': self.gridsize,
224            'potential_name': self.potential_name,
225            'potential_constants': self.potential_constants.tolist() if isinstance(self.potential_constants, np.ndarray) else self.potential_constants,
226            'potential_offset': self.potential_offset,
227            'potential_min': self.potential_min,
228            'potential_max': self.potential_max,
229            'eigenvalues': self.eigenvalues.tolist() if isinstance(self.eigenvalues, np.ndarray) else self.eigenvalues,
230            'E_levels': self.E_levels,
231            'deg': self.deg,
232            'excitations': self.excitations,
233            'splittings': self.splittings,
234            'E_activation': self.E_activation,
235            'runtime': self.runtime,
236        }

Returns a dict with a summary of the System data.