def get_chi_dependence(
        self,
        temperatures=None,
        eigenvalues=None,
        eigenfunctions=None,
    ):
        """
        Calculates the susceptibility at a specified range of temperatures.

        """
        temperatures = utils.get_default(
            temperatures, linspace(1, 300, 300, dtype='float64'))
        if eigenvalues is None and eigenfunctions is None:
            eigenvalues, eigenfunctions = self.get_eigenvalues_and_eigenfunctions(
            )
        temperatures = utils.get_default(
            temperatures, linspace(1, 300, 300, dtype='float64'))
        chi_curie = {
            'z': None,
            'x': None,
        }
        chi_van_vleck = {
            'z': None,
            'x': None,
        }
        chi = {
            'z': None,
            'x': None,
            'total': None,
            'inverse': None,
        }
        for key in ('z', 'x'):
            chi_curie[key] = utils.get_empty_matrix(temperatures.shape)
            chi_van_vleck[key] = utils.get_empty_matrix(temperatures.shape)
            chi[key] = utils.get_empty_matrix(temperatures.shape)

        chi = {
            'total': utils.get_empty_matrix(temperatures.shape),
            'inverse': utils.get_empty_matrix(temperatures.shape),
        }
        for _, temperature in enumerate(temperatures):
            current_chi = self.get_chi(
                temperature,
                eigenvalues,
                eigenfunctions,
            )
            for key in ('z', 'x'):
                chi_curie[key] = current_chi['curie'][key]
                chi_van_vleck[key] = current_chi['van_vleck'][key]
                chi[key] = chi_curie[key] + chi_van_vleck[key]
            chi['total'] = (chi['z'] + 2 * chi['x']) / 3
            chi['inverse'] = 1 / chi['total']

        return chi_curie, chi_van_vleck, chi
 def get_cef_hamiltonian(self, size: int, j: float, squared_j: float):
     """Determines the CEF Hamiltonian based on the input parameters."""
     hamiltonian = utils.get_empty_matrix(size)
     parameters = self.parameters
     for row in range(size):
         # row = 0...2J
         # mqn_1[1] = m = -J...J
         mqn_1 = [(row - j)**i for i in range(5)]
         for key in ('20', '40', '60'):
             hamiltonian[row, row] += (parameters[f'B{key}'] *
                                       physics.steven_operators(
                                           f'o{key}',
                                           squared_j,
                                           mqn_1,
                                       ))
         for degree in range(2, size - row):
             mqn_2 = [(row - j + degree)**i for i in range(5)]
             for key in ('22', '42', '62', '43', '63', '44', '64', '66'):
                 if key[-1] == str(degree):
                     hamiltonian[row,
                                 row + degree] += (parameters[f'B{key}'] *
                                                   physics.steven_operators(
                                                       f'o{key}',
                                                       squared_j,
                                                       mqn_1,
                                                       mqn_2,
                                                   ))
             hamiltonian[row + degree, row] = hamiltonian[row, row + degree]
     return hamiltonian
 def get_zeeman_hamiltonian(self,
                            size: int,
                            j: float,
                            squared_j: float,
                            magnet_field: dict = None):
     """Determines the Zeeman terms to the Hamiltonian."""
     if magnet_field is None:
         magnet_field = self.magnet_field
     hamiltonian = utils.get_empty_matrix(size)
     for row in range(size):
         # mqn_1 =  m = -J...J
         mqn_1 = row - j
         hamiltonian[row,
                     row] -= (self.material.rare_earth.lande_factor *
                              BOHR_MAGNETON * mqn_1 * magnet_field['z'])
         if row < (size - 1):
             column = row + 1
             mqn_2 = mqn_1 + 1
             hamiltonian[row,
                         column] -= (0.5 *
                                     self.material.rare_earth.lande_factor *
                                     BOHR_MAGNETON * sqrt(
                                         (squared_j - mqn_1 * mqn_2)) *
                                     magnet_field['x'])
             hamiltonian[column, row] = hamiltonian[row, column]
     return hamiltonian
 def get_bolzmann_factor(
     self,
     size: int,
     eigenvalues,
     temperature=None,
 ):
     """Determines bolzmann_factor at specified temperature."""
     temperature = utils.get_default(temperature or self.temperature)
     thermal = physics.thermodynamics(temperature, eigenvalues)
     bolzmann_factor = utils.get_empty_matrix(size, dimension=1)
     if thermal['temperature'] <= 0:
         bolzmann_factor[0] = 1
     else:
         bolzmann_factor = thermal['bolzmann'] / sum(thermal['bolzmann'])
     return bolzmann_factor
    def get_spectrum(self,
                     energies=None,
                     temperature=None,
                     width_dict: dict = None,
                     magnet_field: dict = None):
        """Calculates the neutron scattering cross section."""
        temperature = utils.get_default(temperature, self.temperature)
        peaks = self.get_peaks(temperature, magnet_field)
        eigenvalues, _ = self.get_eigenvalues_and_eigenfunctions()

        if energies is None:
            # 501 numbers in range from -1.1*E_max to 1.1*E_max
            energies = linspace(-1.1 * eigenvalues[-1], 1.1 * eigenvalues[-1],
                                501)
        if width_dict is None:
            width_dict = {'sigma': 0.01 * (max(energies) - min(energies))}

        spectrum = utils.get_empty_matrix(energies.size, dimension=1)

        sigma = width_dict.get('sigma', None)
        gamma = width_dict.get('gamma', None)
        for peak in peaks:
            if sigma and not gamma:
                spectrum += peak[1] * physics.gaussian_normalized(
                    energies,
                    peak[0],
                    sigma,
                )
            elif gamma and not sigma:
                spectrum += peak[1] * physics.lorentzian_normalized(
                    energies,
                    peak[0],
                    gamma,
                )
            elif sigma and gamma:
                spectrum += peak[1] * physics.pseudo_voigt_normalized(
                    energies,
                    peak[0],
                    sigma,
                    gamma,
                )

        spectrum *= 72.65 * self.material.rare_earth.lande_factor**2

        return spectrum
    def get_transition_probabilities(
        self,
        eigenfunctions,
    ):
        """
        Determines matrix elements for dipole transitions
        between eigenfunctions of the total Hamiltonian.

        """
        j = self.material.rare_earth.total_momentum_ground
        squared_j = j * (j + 1)
        size = int(2 * j + 1)
        j_ops = {
            'z': utils.get_empty_matrix(size),
            '+': utils.get_empty_matrix(size),
            '-': utils.get_empty_matrix(size),
        }
        transition_probability = utils.get_empty_matrix(size)
        for row in range(size):
            j_ops['z'][row, row] += (eigenfunctions[size - 1, row]**2 *
                                     (size - 1 - j))
            for row_j in range(size - 1):
                j_ops['z'][row, row] += (eigenfunctions[row_j, row]**2 *
                                         (row_j - j))
                j_ops['+'][row, row] += (eigenfunctions[row_j + 1, row] *
                                         eigenfunctions[row_j, row] *
                                         sqrt(squared_j - (row_j - j) *
                                              (row_j - j + 1)))

            j_ops['-'][row, row] = j_ops['+'][row, row]
            for column in range(row + 1, size):
                mqn_1 = size - 1 - j
                j_ops['z'][row, column] += (eigenfunctions[size - 1, row] *
                                            eigenfunctions[size - 1, column] *
                                            mqn_1)
                for row_j in range(size - 1):
                    mqn_1 = row_j - j
                    j_ops['z'][row, column] += (eigenfunctions[row_j, row] *
                                                eigenfunctions[row_j, column] *
                                                mqn_1)
                    column_j = row_j + 1
                    mqn_2 = column_j - j
                    common_root = sqrt(squared_j - mqn_1 * mqn_2)
                    j_ops['+'][row, column] += (eigenfunctions[column_j, row] *
                                                eigenfunctions[row_j, column] *
                                                common_root)
                    j_ops['-'][row,
                               column] += (eigenfunctions[row_j, row] *
                                           eigenfunctions[column_j, column] *
                                           common_root)

                transition_probability[row, column] = (
                    (2 * j_ops['z'][row, column]**2 +
                     j_ops['+'][row, column]**2 + j_ops['-'][row, column]**2) /
                    3)
                j_ops['z'][column, row] = j_ops['z'][row, column]
                j_ops['+'][column, row] = j_ops['-'][row, column]
                j_ops['-'][column, row] = j_ops['+'][row, column]
                transition_probability[column,
                                       row] = transition_probability[row,
                                                                     column]

        return j_ops, transition_probability