25#include <Eigen/SparseCore>
30#include <spdlog/spdlog.h>
34template <
typename Scalar>
36 std::vector<Eigen::SparseMatrix<Scalar, Eigen::RowMajor>>
d1;
37 std::vector<Eigen::SparseMatrix<Scalar, Eigen::RowMajor>>
d2;
38 std::vector<Eigen::SparseMatrix<Scalar, Eigen::RowMajor>>
q1;
39 std::vector<Eigen::SparseMatrix<Scalar, Eigen::RowMajor>>
q2;
42template <
typename Scalar>
45 int interaction_order) {
55 Eigen::Map<const Eigen::Vector3<real_t>> vector_map(distance_vector.data(),
56 distance_vector.size());
57 real_t distance = vector_map.norm();
58 SPDLOG_DEBUG(
"Interatomic distance: {}", distance);
59 if (distance == std::numeric_limits<real_t>::infinity()) {
65 if (interaction_order >= 3) {
67 3 * vector_normalized * vector_normalized.transpose();
68 entries /= std::pow(distance, 3);
70 green_tensor.
set_entries(1, 1, entries.template cast<Scalar>());
74 if (interaction_order >= 4) {
75 Eigen::Matrix<real_t, 3, 9> entries = Eigen::Matrix<real_t, 3, 9>::Zero();
76 for (Eigen::Index q = 0; q < 3; ++q) {
78 for (Eigen::Index j = 0; j < 3; ++j) {
79 for (Eigen::Index i = 0; i < 3; ++i) {
80 Eigen::Index col = 3 * j + i;
82 15 * vector_normalized[q] * vector_normalized[j] * vector_normalized[i];
84 entries(row, col) += -3 * vector_normalized[q];
87 entries(row, col) += -3 * vector_normalized[j];
90 entries(row, col) += -3 * vector_normalized[i];
95 entries /= std::pow(distance, 4);
97 green_tensor.
set_entries(1, 2, entries.template cast<Scalar>());
101 if (interaction_order >= 4) {
102 Eigen::Matrix<real_t, 9, 3> entries = Eigen::Matrix<real_t, 9, 3>::Zero();
103 for (Eigen::Index q = 0; q < 3; ++q) {
104 for (Eigen::Index j = 0; j < 3; ++j) {
105 Eigen::Index row = 3 * q + j;
106 for (Eigen::Index i = 0; i < 3; ++i) {
107 Eigen::Index col = i;
109 -15 * vector_normalized[q] * vector_normalized[j] * vector_normalized[i];
111 entries(row, col) += 3 * vector_normalized[q];
114 entries(row, col) += 3 * vector_normalized[j];
117 entries(row, col) += 3 * vector_normalized[i];
122 entries /= std::pow(distance, 4);
124 green_tensor.
set_entries(2, 1, entries.template cast<Scalar>());
128 if (interaction_order >= 5) {
129 SPDLOG_WARN(
"Quadrupole-quadrupole interaction is considered but "
130 "not dipole-octupole interaction although this interaction would be "
131 "of the same order. We plan to implement dipole-octupole interaction "
134 Eigen::Matrix<real_t, 9, 9> entries = Eigen::Matrix<real_t, 9, 9>::Zero();
135 for (Eigen::Index q = 0; q < 3; ++q) {
136 for (Eigen::Index j = 0; j < 3; ++j) {
137 Eigen::Index row = 3 * q + j;
138 for (Eigen::Index i = 0; i < 3; ++i) {
139 for (Eigen::Index k = 0; k < 3; ++k) {
140 Eigen::Index col = 3 * i + k;
141 entries(row, col) += 105 * vector_normalized[q] * vector_normalized[j] *
142 vector_normalized[i] * vector_normalized[k];
144 entries(row, col) += -15 * vector_normalized[q] * vector_normalized[k];
147 entries(row, col) += -15 * vector_normalized[j] * vector_normalized[k];
150 entries(row, col) += -15 * vector_normalized[i] * vector_normalized[k];
153 entries(row, col) += -15 * vector_normalized[j] * vector_normalized[i];
156 entries(row, col) += -15 * vector_normalized[q] * vector_normalized[i];
159 entries(row, col) += -15 * vector_normalized[q] * vector_normalized[j];
161 if (q == k && i == j) {
162 entries(row, col) += 3;
164 if (i == k && j == q) {
165 entries(row, col) += 3;
167 if (j == k && i == q) {
168 entries(row, col) += 3;
174 entries /= std::pow(distance, 5);
176 green_tensor.
set_entries(2, 2, entries.template cast<Scalar>());
182template <
typename Scalar>
183OperatorMatrices<Scalar>
240template <
typename Scalar>
244template <
typename Scalar>
246 this->hamiltonian_requires_construction =
true;
247 if (value < 3 || value > 5) {
248 throw std::invalid_argument(
"The order must be 3, 4, or 5.");
250 interaction_order = value;
254template <
typename Scalar>
256 this->hamiltonian_requires_construction =
true;
258 constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
260 std::abs(distance_vector[1]) > numerical_precision) {
261 throw std::invalid_argument(
262 "The distance vector must not have a y-component if the scalar type is real.");
265 distance_vector = vector;
270template <
typename Scalar>
272 auto basis = this->hamiltonian->
get_basis();
273 auto basis1 = basis->get_basis1();
274 auto basis2 = basis->get_basis2();
276 auto green_tensor = construct_green_tensor<Scalar>(distance_vector, interaction_order);
281 this->hamiltonian_is_diagonal =
true;
282 bool sort_by_quantum_number_f = basis->has_quantum_number_f();
283 bool sort_by_quantum_number_m = basis->has_quantum_number_m();
284 bool sort_by_parity = basis->has_parity();
287 for (
const auto &entry : green_tensor.get_entries(1, 1)) {
289 this->hamiltonian->get_matrix() += ce->val() *
291 if (ce->row() != ce->col()) {
292 sort_by_quantum_number_m =
false;
294 this->hamiltonian_is_diagonal =
false;
295 sort_by_quantum_number_f =
false;
297 throw std::invalid_argument(
"Omega-dependent entries are not yet supported.");
302 for (
const auto &entry : green_tensor.get_entries(1, 2)) {
303 if (
auto ce = std::get_if<
typename GreenTensor<Scalar>::ConstantEntry>(&entry)) {
304 this->hamiltonian->get_matrix() += ce->val() *
306 if (ce->row() != ce->col() - 1) {
307 sort_by_quantum_number_m =
false;
309 this->hamiltonian_is_diagonal =
false;
310 sort_by_quantum_number_f =
false;
312 throw std::invalid_argument(
"Omega-dependent entries are not yet supported.");
317 for (
const auto &entry : green_tensor.get_entries(2, 1)) {
318 if (
auto ce = std::get_if<
typename GreenTensor<Scalar>::ConstantEntry>(&entry)) {
319 this->hamiltonian->get_matrix() += ce->val() *
321 if (ce->row() - 1 != ce->col()) {
322 sort_by_quantum_number_m =
false;
324 this->hamiltonian_is_diagonal =
false;
325 sort_by_quantum_number_f =
false;
327 throw std::invalid_argument(
"Omega-dependent entries are not yet supported.");
332 for (
const auto &entry : green_tensor.get_entries(2, 2)) {
333 if (
auto ce = std::get_if<
typename GreenTensor<Scalar>::ConstantEntry>(&entry)) {
334 this->hamiltonian->get_matrix() += ce->val() *
336 if (ce->row() != ce->col()) {
337 sort_by_quantum_number_m =
false;
339 this->hamiltonian_is_diagonal =
false;
340 sort_by_quantum_number_f =
false;
342 throw std::invalid_argument(
"Omega-dependent entries are not yet supported.");
347 this->blockdiagonalizing_labels.clear();
348 if (sort_by_quantum_number_f) {
351 if (sort_by_quantum_number_m) {
354 if (sort_by_parity) {
360template class SystemPair<double>;
361template class SystemPair<std::complex<double>>;
Class for creating a basis of atomic kets.
const std::vector< Entry > & get_entries(int kappa1, int kappa2) const
void set_entries(int kappa1, int kappa2, const Eigen::MatrixX< Scalar > &tensor_in_cartesian_coordinates)
SystemPair(std::shared_ptr< const basis_t > basis)
typename traits::CrtpTraits< Type >::real_t real_t
Type & set_interaction_order(int value)
Type & set_distance_vector(const std::array< real_t, 3 > &vector)
std::shared_ptr< const basis_t > get_basis() const
Matrix< Type, 3, 1 > Vector3
Matrix< Type, 3, 3 > Matrix3
Eigen::SparseMatrix< Scalar, Eigen::RowMajor > calculate_tensor_product(const std::shared_ptr< const BasisPair< Scalar > > &basis_initial, const std::shared_ptr< const BasisPair< Scalar > > &basis_final, const Eigen::SparseMatrix< Scalar, Eigen::RowMajor > &matrix1, const Eigen::SparseMatrix< Scalar, Eigen::RowMajor > &matrix2)
@ ELECTRIC_QUADRUPOLE_ZERO
GreenTensor< Scalar > construct_green_tensor(const std::array< typename traits::NumTraits< Scalar >::real_t, 3 > &distance_vector, int interaction_order)
OperatorMatrices< Scalar > construct_operator_matrices(const GreenTensor< Scalar > &green_tensor, const std::shared_ptr< const BasisAtom< Scalar > > &basis1, const std::shared_ptr< const BasisAtom< Scalar > > &basis2)
@ SORT_BY_QUANTUM_NUMBER_M
@ SORT_BY_QUANTUM_NUMBER_F
std::vector< Eigen::SparseMatrix< Scalar, Eigen::RowMajor > > q2
std::vector< Eigen::SparseMatrix< Scalar, Eigen::RowMajor > > q1
std::vector< Eigen::SparseMatrix< Scalar, Eigen::RowMajor > > d1
std::vector< Eigen::SparseMatrix< Scalar, Eigen::RowMajor > > d2
Helper struct to extract types from a numerical type.