GQCP
Loading...
Searching...
No Matches
GSpinorBasis.hpp
Go to the documentation of this file.
1// This file is part of GQCG-GQCP.
2//
3// Copyright (C) 2017-2020 the GQCG developers
4//
5// GQCG-GQCP is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GQCG-GQCP is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GQCG-GQCP. If not, see <http://www.gnu.org/licenses/>.
17
18#pragma once
19
20
29#include "Molecule/Molecule.hpp"
47
48
49namespace GQCP {
50
51
60template <typename _ExpansionScalar, typename _Shell>
62 public SimpleSpinorBasis<_ExpansionScalar, GSpinorBasis<_ExpansionScalar, _Shell>> {
63public:
64 // The scalar type used to represent an expansion coefficient of the spinors in the underlying scalar orbitals: real or complex.
65 using ExpansionScalar = _ExpansionScalar;
66
67 // The type of shell the underlying scalar bases contain.
68 using Shell = _Shell;
69
70 // The type of the base spinor basis.
72
73 // The type of transformation that is naturally related to a `GSpinorBasis`.
75
76 // The type that is used for representing the primitive for a basis function of this spin-orbital basis' underlying AO basis.
77 using Primitive = typename Shell::Primitive;
78
79 // The type that is used for representing the underlying basis functions of this spin-orbital basis.
80 using BasisFunction = typename Shell::BasisFunction;
81
82
83private:
84 // The scalar bases for the alpha and beta components of the spinors.
86
87
88public:
89 /*
90 * MARK: Constructors
91 */
92
100 Base(C),
101 scalar_bases {scalar_bases} {
102
103 // Check if the dimensions of the given objects are compatible
104 const auto K_alpha = scalar_bases.alpha().numberOfBasisFunctions();
105 const auto K_beta = scalar_bases.beta().numberOfBasisFunctions();
106
107 if (C.numberOfOrbitals() != K_alpha + K_beta) {
108 throw std::invalid_argument("GSpinorBasis(const SpinResolved<ScalarBasis<Shell>>&, const Transformation&): The given dimensions of the scalar bases and coefficient matrix are incompatible.");
109 }
110 }
111
119 GSpinorBasis(const ScalarBasis<Shell>& alpha_scalar_basis, const ScalarBasis<Shell>& beta_scalar_basis, const Transformation& C) :
120 GSpinorBasis(SpinResolved<ScalarBasis<Shell>> {alpha_scalar_basis, beta_scalar_basis}, C) {}
121
122
129 GSpinorBasis(const ScalarBasis<Shell>& scalar_basis, const Transformation& C) :
130 GSpinorBasis(scalar_basis, scalar_basis, C) {}
131
132
139 GSpinorBasis(const ScalarBasis<Shell>& alpha_scalar_basis, const ScalarBasis<Shell>& beta_scalar_basis) :
140 GSpinorBasis(alpha_scalar_basis, beta_scalar_basis,
141 Transformation::Identity(alpha_scalar_basis.numberOfBasisFunctions(), beta_scalar_basis.numberOfBasisFunctions())) {}
142
143
149 GSpinorBasis(const ScalarBasis<Shell>& scalar_basis) :
150 GSpinorBasis(scalar_basis, scalar_basis) {}
151
152
161 GSpinorBasis(const NuclearFramework& nuclear_framework, const std::string& basisset_name) :
162 GSpinorBasis(ScalarBasis<Shell>(nuclear_framework, basisset_name)) {}
163
164
173 GSpinorBasis(const Molecule& molecule, const std::string& basisset_name) :
174 GSpinorBasis(ScalarBasis<Shell>(molecule.nuclearFramework(), basisset_name)) {}
175
176
186 template <typename Z = Shell>
187 GSpinorBasis(const Molecule& molecule, const std::string& basisset_name, const HomogeneousMagneticField& B,
188 typename std::enable_if<std::is_same<Z, LondonGTOShell>::value>::type* = 0) :
189 GSpinorBasis(ScalarBasis<Shell>(molecule.nuclearFramework(), basisset_name, B)) {}
190
191
201 GSpinorBasis(const NuclearFramework& nuclear_framework, const std::string& basisset_name_alpha, const std::string& basisset_name_beta) :
202 GSpinorBasis(ScalarBasis<Shell>(nuclear_framework, basisset_name_alpha),
203 ScalarBasis<Shell>(nuclear_framework, basisset_name_beta)) {}
204
205
215 GSpinorBasis(const Molecule& molecule, const std::string& basisset_name_alpha, const std::string& basisset_name_beta) :
216 GSpinorBasis(ScalarBasis<Shell>(molecule.nuclearFramework(), basisset_name_alpha),
217 ScalarBasis<Shell>(molecule.nuclearFramework(), basisset_name_beta)) {}
218
219
220 /*
221 * MARK: Named constructors
222 */
223
232
233 // Create an USpinOrbitalBasis from the restricted one and use ::FromUnrestricted.
234 const auto u_spinor_basis = USpinOrbitalBasis<ExpansionScalar, Shell>::FromRestricted(r_spinor_basis);
235
237 }
238
239
250
251 const auto C_general = GTransformation<ExpansionScalar>::FromUnrestricted(u_spinor_basis.expansion());
252
253 return GSpinorBasis<ExpansionScalar, Shell>(u_spinor_basis.alpha().scalarBasis(), C_general); // Assume the alpha- and beta- scalar bases are equal.
254 }
255
256
257 /*
258 * MARK: Coefficients
259 */
260
266 size_t numberOfCoefficients(const Spin& sigma) const { return this->scalarBases().component(sigma).numberOfBasisFunctions(); }
267
268
269 /*
270 * MARK: General info
271 */
272
276 size_t numberOfSpinors() const {
277
278 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
279 const auto K_beta = this->numberOfCoefficients(Spin::beta);
280
281 return K_alpha + K_beta;
282 }
283
284
285 /*
286 * MARK: Quantization of first-quantized operators (GTOShell)
287 */
288
296 template <typename FQOneElectronOperator, typename Z = Shell>
297 auto quantize(const FQOneElectronOperator& fq_one_op) const -> enable_if_t<std::is_same<Z, GTOShell>::value, GSQOneElectronOperator<product_t<typename FQOneElectronOperator::Scalar, ExpansionScalar>, typename FQOneElectronOperator::Vectorizer>> {
298
301
302
303 // The strategy for calculating the matrix representation of the one-electron operator in this spinor basis is to:
304 // 1. Express the operator in the underlying scalar bases; and
305 // 2. Afterwards transform them using the current coefficient matrix.
306 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
307 const auto K_beta = this->numberOfCoefficients(Spin::beta);
308 const auto M = this->numberOfSpinors();
310
311 // 1. Express the operator in the underlying scalar bases: spin-independent operators only have alpha-alpha and beta-beta blocks.
312 const auto F_alpha = IntegralCalculator::calculateLibintIntegrals(fq_one_op, this->scalarBases().alpha());
313 const auto F_beta = IntegralCalculator::calculateLibintIntegrals(fq_one_op, this->scalarBases().beta());
314
315 f.topLeftCorner(K_alpha, K_alpha) = F_alpha;
316 f.bottomRightCorner(K_beta, K_beta) = F_beta;
317
318 // 2. Transform using the current coefficient matrix.
319 ResultOperator op {{f}}; // op for 'operator'
320 op.transform(this->expansion());
321 return op;
322 }
323
324
332 template <typename Z = Shell>
334
337
338 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
339 const auto K_beta = this->numberOfCoefficients(Spin::beta);
340 const auto M = this->numberOfSpinors();
341
342 // The strategy to quantize the spin operator is as follows.
343 // 1. First, calculate the necessary overlap integrals over the scalar bases.
344 // 2. Then, construct the scalar basis representations of the components of the spin operator by placing the overlaps into the correct blocks.
345 // 3. Transform the components (in scalar basis) with the current coefficient matrix to yield the components in spinor basis.
346
350
351
352 // 1. Calculate the necessary overlap integrals over the scalar bases.
357
358
359 // 2. Place the overlaps into the correct blocks.
360 S_x.block(0, K_alpha, K_alpha, K_beta) = 0.5 * S_ab;
361 S_x.block(K_alpha, 0, K_beta, K_alpha) = 0.5 * S_ba;
362
363 using namespace GQCP::literals;
364 S_y.block(0, K_alpha, K_alpha, K_beta) = -0.5 * 1_ii * S_ab;
365 S_y.block(K_alpha, 0, K_beta, K_alpha) = 0.5 * 1_ii * S_ba;
366
367 S_z.topLeftCorner(K_alpha, K_alpha) = 0.5 * S_aa;
368 S_z.bottomRightCorner(K_beta, K_beta) = -0.5 * S_bb;
369
370
371 // 3. Transform using the coefficient matrix
372 ResultOperator spin_op {std::vector<SquareMatrix<ResultScalar>> {S_x, S_y, S_z}}; // 'op' for operator
373 spin_op.transform(this->expansion());
374 return spin_op;
375 }
376
377
385 template <typename Z = Shell>
387
390
391 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
392 const auto K_beta = this->numberOfCoefficients(Spin::beta);
393 const auto M = this->numberOfSpinors();
394
395 // The strategy to quantize the spin operator is as follows.
396 // 1. First, calculate the necessary overlap integrals over the scalar bases.
397 // 2. Then, construct the scalar basis representations of the components of the spin operator by placing the overlaps into the correct blocks.
398 // 3. Transform the components (in scalar basis) with the current coefficient matrix to yield the components in spinor basis.
400
401 // 1. Calculate the necessary overlap integrals over the scalar bases.
404
405 // 2. Place the overlaps into the correct blocks.
406 S_z.topLeftCorner(K_alpha, K_alpha) = 0.5 * S_aa;
407 S_z.bottomRightCorner(K_beta, K_beta) = -0.5 * S_bb;
408
409 // 3. Transform using the coefficient matrix.
410 ResultOperator spin_op {{S_z}}; // 'op' for operator
411 spin_op.transform(this->expansion());
412 return spin_op;
413 }
414
415
423 template <typename Z = Shell>
425
428
429 // The strategy for calculating the matrix representation of the two-electron operator in this spinor basis is to:
430 // 1. Calculate the Coulomb integrals in the underlying scalar bases;
431 // 2. Place the calculated integrals as 'blocks' in the larger representation, so that we can;
432 // 3. Transform the operator using the current coefficient matrix.
433
434 // 1. Calculate the Coulomb integrals in the underlying scalar bases.
439
440
441 // 2. Place the calculated integrals as 'blocks' in the larger representation
442 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
443 const auto K_beta = this->numberOfCoefficients(Spin::beta);
444
445 const auto M = this->numberOfSpinors();
446 auto g_par = SquareRankFourTensor<ResultScalar>::Zero(M); // 'par' for 'parameters'
447
448 // Primed indices are indices in the larger representation, normal ones are those in the smaller tensors.
449 for (size_t mu_ = 0; mu_ < M; mu_++) { // mu 'prime'
450 const size_t mu = mu_ % K_alpha;
451
452 for (size_t nu_ = 0; nu_ < M; nu_++) { // nu 'prime'
453 const size_t nu = nu_ % K_alpha;
454
455 for (size_t rho_ = 0; rho_ < M; rho_++) { // rho 'prime'
456 const size_t rho = rho_ % K_alpha;
457
458 for (size_t lambda_ = 0; lambda_ < M; lambda_++) { // lambda 'prime'
459 const size_t lambda = lambda_ % K_alpha;
460
461 if ((mu_ < K_alpha) && (nu_ < K_alpha) && (rho_ < K_alpha) && (lambda_ < K_alpha)) {
462 g_par(mu_, nu_, rho_, lambda_) = g_aaaa(mu, nu, rho, lambda);
463 } else if ((mu_ < K_alpha) && (nu_ < K_alpha) && (rho_ >= K_alpha) && (lambda_ >= K_alpha)) {
464 g_par(mu_, nu_, rho_, lambda_) = g_aabb(mu, nu, rho, lambda);
465 } else if ((mu_ >= K_alpha) && (nu_ >= K_alpha) && (rho_ < K_alpha) && (lambda_ < K_alpha)) {
466 g_par(mu_, nu_, rho_, lambda_) = g_bbaa(mu, nu, rho, lambda);
467 } else if ((mu_ >= K_alpha) && (nu_ >= K_alpha) && (rho_ >= K_alpha) && (lambda_ >= K_alpha)) {
468 g_par(mu_, nu_, rho_, lambda_) = g_bbbb(mu, nu, rho, lambda);
469 }
470 }
471 }
472 }
473 }
474
475
476 // 3. Transform the operator using the current coefficient matrix.
477 ResultOperator g_op {g_par}; // 'op' for 'operator'
478 g_op.transform(this->expansion());
479 return g_op;
480 }
481
482
483 /*
484 * MARK: Quantization of first-quantized operators (LondonGTOShell)
485 */
486
494 template <typename FQOneElectronOperator, typename Z = Shell>
495 auto quantize(const FQOneElectronOperator& fq_one_op) const -> enable_if_t<std::is_same<Z, LondonGTOShell>::value, GSQOneElectronOperator<product_t<typename FQOneElectronOperator::Scalar, ExpansionScalar>, typename FQOneElectronOperator::Vectorizer>> {
496
499 using Vectorizer = typename FQOneElectronOperator::Vectorizer;
500
501 const auto N = FQOneElectronOperator::NumberOfComponents;
502 const auto& vectorizer = FQOneElectronOperator::vectorizer;
503
504
505 // The strategy for calculating the matrix representation of the one-electron operator in this spinor basis is to:
506 // 1. Express the operator in the underlying scalar bases and;
507 // 2. Afterwards transform them using the current coefficient matrix.
508 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
509 const auto K_beta = this->numberOfCoefficients(Spin::beta);
510 const auto M = this->numberOfSpinors();
511
512
513 // 1. Express the operator in the underlying scalar bases: spin-independent operators only have alpha-alpha and beta-beta blocks.
514 auto engine = GQCP::IntegralEngine::InHouse<GQCP::LondonGTOShell>(fq_one_op);
515 const auto F_aa = GQCP::IntegralCalculator::calculate(engine, this->scalarBases().alpha().shellSet(), this->scalarBases().alpha().shellSet());
516 const auto F_bb = GQCP::IntegralCalculator::calculate(engine, this->scalarBases().beta().shellSet(), this->scalarBases().beta().shellSet());
517
518 // For each of the components of the operator, place the scalar basis representations into the spinor basis representation.
519 std::array<SquareMatrix<ResultScalar>, N> fs;
520 for (size_t i = 0; i < N; i++) {
522
523 f.topLeftCorner(K_alpha, K_alpha) = F_aa[i];
524 f.bottomRightCorner(K_beta, K_beta) = F_bb[i];
525
526 fs[i] = f;
527 }
528
529
530 // 2. Transform using the current coefficient matrix.
531 StorageArray<SquareMatrix<ResultScalar>, Vectorizer> array {fs, vectorizer};
532 ResultOperator op {array}; // 'op' for 'operator'.
533 op.transform(this->expansion());
534 return op;
535 }
536
537
545 template <typename Z = Shell>
547
550
551 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
552 const auto K_beta = this->numberOfCoefficients(Spin::beta);
553 const auto M = this->numberOfSpinors();
554
555 // The strategy to quantize the spin operator is as follows.
556 // 1. First, calculate the necessary overlap integrals over the scalar bases.
557 // 2. Then, construct the scalar basis representations of the components of the spin operator by placing the overlaps into the correct blocks.
558 // 3. Transform the components (in scalar basis) with the current coefficient matrix to yield the components in spinor basis.
559
563
564
565 // 1. Calculate the necessary overlap integrals over the scalar bases.
566 auto overlap_engine = GQCP::IntegralEngine::InHouse<GQCP::LondonGTOShell>(OverlapOperator());
567 const auto S_aa = GQCP::IntegralCalculator::calculate(overlap_engine, this->scalarBases().alpha().shellSet(), this->scalarBases().alpha().shellSet())[0];
568 const auto S_ab = GQCP::IntegralCalculator::calculate(overlap_engine, this->scalarBases().alpha().shellSet(), this->scalarBases().beta().shellSet())[0];
569 const auto S_ba = GQCP::IntegralCalculator::calculate(overlap_engine, this->scalarBases().beta().shellSet(), this->scalarBases().alpha().shellSet())[0];
570 const auto S_bb = GQCP::IntegralCalculator::calculate(overlap_engine, this->scalarBases().beta().shellSet(), this->scalarBases().beta().shellSet())[0];
571
572
573 // 2. Place the overlaps into the correct blocks.
574 S_x.block(0, K_alpha, K_alpha, K_beta) = 0.5 * S_ab;
575 S_x.block(K_alpha, 0, K_beta, K_alpha) = 0.5 * S_ba;
576
577 using namespace GQCP::literals;
578 S_y.block(0, K_alpha, K_alpha, K_beta) = -0.5 * 1_ii * S_ab;
579 S_y.block(K_alpha, 0, K_beta, K_alpha) = 0.5 * 1_ii * S_ba;
580
581 S_z.topLeftCorner(K_alpha, K_alpha) = 0.5 * S_aa;
582 S_z.bottomRightCorner(K_beta, K_beta) = -0.5 * S_bb;
583
584
585 // 3. Transform using the coefficient matrix.
586 ResultOperator spin_op {std::vector<SquareMatrix<ResultScalar>> {S_x, S_y, S_z}}; // 'op' for operator.
587 spin_op.transform(this->expansion());
588 return spin_op;
589 }
590
591
599 template <typename Z = Shell>
601
602 // Return the orbital Zeeman operator as a contraction beween the magnetic field and the angular momentum operator.
603 const auto L = this->quantize(op.angularMomentum());
604 const auto& B = op.magneticField().strength();
605 return 0.5 * L.dot(B);
606 }
607
608
616 template <typename Z = Shell>
618
621
622
623 // Return the diamagnetic operator as a contraction beween the magnetic field and the electronic quadrupole operator.
624 const auto Q = this->quantize(op.electronicQuadrupole()).allParameters();
625 const auto& B = op.magneticField().strength();
626
627 // Prepare some variables.
628 const auto& B_x = B(CartesianDirection::x);
629 const auto& B_y = B(CartesianDirection::y);
630 const auto& B_z = B(CartesianDirection::z);
631
632 const auto& Q_xx = Q[DyadicCartesianDirection::xx];
633 const auto& Q_xy = Q[DyadicCartesianDirection::xy];
634 const auto& Q_xz = Q[DyadicCartesianDirection::xz];
635 const auto& Q_yy = Q[DyadicCartesianDirection::yy];
636 const auto& Q_yz = Q[DyadicCartesianDirection::yz];
637 const auto& Q_zz = Q[DyadicCartesianDirection::zz];
638
639
640 SquareMatrix<ResultScalar> D_par = 0.125 * ((std::pow(B_y, 2) + std::pow(B_z, 2)) * Q_xx +
641 (std::pow(B_x, 2) + std::pow(B_z, 2)) * Q_yy +
642 (std::pow(B_x, 2) + std::pow(B_y, 2)) * Q_zz -
643 2 * B_x * B_y * Q_xy -
644 2 * B_x * B_z * Q_xz -
645 2 * B_y * B_z * Q_yz);
646
647 return ResultOperator {D_par};
648 }
649
650
658 template <typename Z = Shell>
660
661 // Return the spin Zeeman operator as a contraction beween the magnetic field and the spin operator.
662 const auto S = this->quantize(ElectronicSpinOperator());
663 const auto& B = op.magneticField().strength();
664 return S.dot(B);
665 }
666
667
677 template <typename Z = Shell>
679
682
683 // The strategy for calculating the matrix representation of the two-electron operator in this spinor basis is to:
684 // 1. Calculate the Coulomb integrals in the underlying scalar bases;
685 // 2. Place the calculated integrals as 'blocks' in the larger representation, so that we can;
686 // 3. Transform the operator using the current coefficient matrix.
687
688 // 1. Calculate the Coulomb integrals in the underlying scalar bases.
689 auto coulomb_engine = GQCP::IntegralEngine::InHouse<GQCP::LondonGTOShell>(CoulombRepulsionOperator());
690 const auto g = GQCP::IntegralCalculator::calculate(coulomb_engine, this->scalarBases().alpha().shellSet(), this->scalarBases().alpha().shellSet())[0];
691
692
693 // 2. Place the calculated integrals as 'blocks' in the larger representation.
694 const auto K_alpha = this->numberOfCoefficients(Spin::alpha);
695 const auto K_beta = this->numberOfCoefficients(Spin::beta);
696
697 const auto M = this->numberOfSpinors();
698 auto g_par = SquareRankFourTensor<ResultScalar>::Zero(M); // 'par' for 'parameters'.
699
700 // Primed indices are indices in the larger representation, normal ones are those in the smaller tensors.
701 for (size_t mu_ = 0; mu_ < M; mu_++) { // Mu 'prime'.
702 const size_t mu = mu_ % K_alpha;
703
704 for (size_t nu_ = 0; nu_ < M; nu_++) { // Nu 'prime'.
705 const size_t nu = nu_ % K_alpha;
706
707 for (size_t rho_ = 0; rho_ < M; rho_++) { // Rho 'prime'.
708 const size_t rho = rho_ % K_alpha;
709
710 for (size_t lambda_ = 0; lambda_ < M; lambda_++) { // Lambda 'prime'.
711 const size_t lambda = lambda_ % K_alpha;
712
713 if ((mu_ < K_alpha) && (nu_ < K_alpha) && (rho_ < K_alpha) && (lambda_ < K_alpha)) {
714 g_par(mu_, nu_, rho_, lambda_) = g(mu, nu, rho, lambda);
715 } else if ((mu_ < K_alpha) && (nu_ < K_alpha) && (rho_ >= K_alpha) && (lambda_ >= K_alpha)) {
716 g_par(mu_, nu_, rho_, lambda_) = g(mu, nu, rho, lambda);
717 } else if ((mu_ >= K_alpha) && (nu_ >= K_alpha) && (rho_ < K_alpha) && (lambda_ < K_alpha)) {
718 g_par(mu_, nu_, rho_, lambda_) = g(mu, nu, rho, lambda);
719 } else if ((mu_ >= K_alpha) && (nu_ >= K_alpha) && (rho_ >= K_alpha) && (lambda_ >= K_alpha)) {
720 g_par(mu_, nu_, rho_, lambda_) = g(mu, nu, rho, lambda);
721 }
722 }
723 }
724 }
725 }
726
727
728 // 3. Transform the operator using the current coefficient matrix.
729 ResultOperator g_op {g_par}; // 'op' for 'operator'.
730 g_op.transform(this->expansion());
731 return g_op;
732 }
733
734
742 template <typename Z = Shell>
744
745 const auto T = this->quantize(fq_hamiltonian.kinetic());
746 const auto OZ = this->quantize(fq_hamiltonian.orbitalZeeman());
747 const auto D = this->quantize(fq_hamiltonian.diamagnetic());
748
749 const auto V = this->quantize(fq_hamiltonian.nuclearAttraction());
750
751 const auto g = this->quantize(fq_hamiltonian.coulombRepulsion());
752
753 return GSQHamiltonian<ExpansionScalar> {{T, OZ, D, V}, {g}};
754 }
755
756
764 template <typename Z = Shell>
766
767 const auto T = this->quantize(fq_hamiltonian.kinetic());
768 const auto OZ = this->quantize(fq_hamiltonian.orbitalZeeman());
769 const auto D = this->quantize(fq_hamiltonian.diamagnetic());
770
771 const auto SZ = this->quantize(fq_hamiltonian.spinZeeman());
772
773 const auto V = this->quantize(fq_hamiltonian.nuclearAttraction());
774
775 const auto g = this->quantize(fq_hamiltonian.coulombRepulsion());
776
777 return GSQHamiltonian<ExpansionScalar> {{T, OZ, D, SZ, V}, {g}};
778 }
779
780
781 /*
782 * MARK: Quantization of first-quantized operators
783 */
784
793
794 const auto T = this->quantize(fq_hamiltonian.kinetic());
795 const auto V = this->quantize(fq_hamiltonian.nuclearAttraction());
796
797 const auto g = this->quantize(fq_hamiltonian.coulombRepulsion());
798
799 return GSQHamiltonian<ExpansionScalar> {T + V, g};
800 }
801
802
812 template <typename S = ExpansionScalar>
814
815 // Prepare the 3 vector components of the electronic spin operator S = (S_x, S_y, S_z).
816 const auto S_op = this->quantize(ElectronicSpinOperator());
817 const auto& S_x_op = S_op(CartesianDirection::x);
818 const auto& S_y_op = S_op(CartesianDirection::y);
819 const auto& S_z_op = S_op(CartesianDirection::z);
820
821
822 // Calculate S^2 = S_x^2 + S_y^2 + S_z^2.
823 const auto S_x_2 = S_x_op * S_x_op;
824 const auto S_y_2 = S_y_op * S_y_op;
825 const auto S_z_2 = S_z_op * S_z_op;
826
827 return S_x_2 + S_y_2 + S_z_2;
828 }
829
830
838 template <typename S = ExpansionScalar>
840 // Define the dimensions and empty matrices.
841 const auto K = this->numberOfCoefficients(Spin::alpha);
842 const auto M = this->numberOfSpinors();
843
848
849 // Calculate the necessary overlap integrals over the scalar bases.
851
852 // Calculate the S_z operator.
853 const auto S_z = this->quantize(ElectronicSpin_zOperator()).parameters();
854
855 // Fill in the overlap in the correct positions.
856 S_plus.topRightCorner(K, K) = S_aa;
857 S_minus.bottomLeftCorner(K, K) = S_aa;
858 S_plus_minus.bottomRightCorner(K, K) = S_aa;
859
860 S_z_squared.topLeftCorner(K, K) = 0.25 * S_aa;
861 S_z_squared.bottomRightCorner(K, K) = 0.25 * S_aa;
862
863 // Create the one electron part.
864 ScalarGSQOneElectronOperator<double> one_electron_part {S_z_squared + S_z + S_plus_minus};
865
866 // Determine the tensors of the two-electron contributions.
869 for (size_t p = 0; p < M; p++) {
870 for (size_t q = 0; q < M; q++) {
871 for (size_t r = 0; r < M; r++) {
872 for (size_t s = 0; s < M; s++) {
873 T_S_z(p, q, r, s) = 2.0 * S_z(p, q) * S_z(r, s); // Include the prefactor '2' because we're going to encapsulate these matrix elements with a `ScalarGSQTwoElectronOperator`, whose matrix elements should not embet the prefactor 0.5 for two-electron operators.
874 T_S_minus_plus(p, q, r, s) = 2.0 * S_minus(p, q) * S_plus(r, s); // Include the prefactor '2' because we're going to encapsulate these matrix elements with a `ScalarGSQTwoElectronOperator`, whose matrix elements should not embet the prefactor 0.5 for two-electron operators.
875 }
876 }
877 }
878 }
879 // Create the two electron part.
881
882 return ScalarGSQOneElectronOperatorProduct<double>(one_electron_part, two_electron_part);
883 }
884
885
886 /*
887 * MARK: Scalar basis
888 */
889
893 const SpinResolved<ScalarBasis<Shell>>& scalarBases() const { return this->scalar_bases; }
894
895
909 GMullikenDomain<ExpansionScalar> mullikenDomain(const std::function<bool(const BasisFunction&)>& selector) const {
910
911 // Assume the underlying scalar bases are equal, and proceed to work with the one for the alpha component.
912 const auto ao_indices = this->scalarBases().alpha().basisFunctionIndices(selector);
913 return GMullikenDomain<ExpansionScalar> {ao_indices, ao_indices.size()};
914 }
915
916
926 GMullikenDomain<ExpansionScalar> mullikenDomain(const std::function<bool(const Shell&)>& selector) const {
927
928 // Assume the underlying scalar bases are equal, and proceed to work with the one for the alpha component.
929 const auto ao_indices = this->scalarBases().alpha().basisFunctionIndices(selector);
930 return GMullikenDomain<ExpansionScalar> {ao_indices, ao_indices.size()};
931 }
932};
933
934
935/*
936 * MARK: SpinorBasisTraits
937 */
938
942template <typename _ExpansionScalar, typename _Shell>
943struct SpinorBasisTraits<GSpinorBasis<_ExpansionScalar, _Shell>> {
944 // The scalar type used to represent an expansion coefficient of the spinors in the underlying scalar orbitals: real or complex.
945 using ExpansionScalar = _ExpansionScalar;
946
947 // The type of transformation that is naturally related to a `GSpinorBasis`.
949
950 // The second-quantized representation of the overlap operator related to the derived spinor basis.
952};
953
954
955/*
956 * MARK: BasisTransformableTraits
957 */
958
962template <typename _ExpansionScalar, typename _Shell>
963struct BasisTransformableTraits<GSpinorBasis<_ExpansionScalar, _Shell>> {
964
965 // The type of transformation that is naturally related to a `GSpinorBasis`.
967};
968
969
970/*
971 * MARK: JacobiRotatableTraits
972 */
973
977template <typename _ExpansionScalar, typename _Shell>
978struct JacobiRotatableTraits<GSpinorBasis<_ExpansionScalar, _Shell>> {
979
980 // The type of Jacobi rotation that is naturally related to a `GSpinorBasis`.
982};
983
984
985} // namespace GQCP
_Vectorizer Vectorizer
Definition: BaseFQOperator.hpp:48
Definition: CoulombRepulsionOperator.hpp:31
Definition: DenseVectorizer.hpp:49
Definition: DiamagneticOperator.hpp:35
Definition: ElectronicSpin_zOperator.hpp:31
Definition: ElectronicSpinOperator.hpp:32
Definition: ElectronicSpinSquaredOperator.hpp:32
Definition: FQMolecularHamiltonian.hpp:33
const KineticOperator & kinetic() const
Definition: FQMolecularHamiltonian.hpp:73
const NuclearAttractionOperator & nuclearAttraction() const
Definition: FQMolecularHamiltonian.hpp:78
const CoulombRepulsionOperator & coulombRepulsion() const
Definition: FQMolecularHamiltonian.hpp:83
Definition: FQMolecularMagneticHamiltonian.hpp:35
const DiamagneticOperator & diamagnetic() const
Definition: FQMolecularMagneticHamiltonian.hpp:80
const OrbitalZeemanOperator & orbitalZeeman() const
Definition: FQMolecularMagneticHamiltonian.hpp:75
Definition: FQMolecularPauliHamiltonian.hpp:32
const SpinZeemanOperator & spinZeeman() const
Definition: FQMolecularPauliHamiltonian.hpp:70
Definition: GMullikenDomain.hpp:35
Definition: GSQOneElectronOperator.hpp:42
Definition: GSQTwoElectronOperator.hpp:43
Definition: GSpinorBasis.hpp:62
GSpinorBasis(const Molecule &molecule, const std::string &basisset_name_alpha, const std::string &basisset_name_beta)
Definition: GSpinorBasis.hpp:215
enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQHamiltonian< ExpansionScalar > > quantize(const FQMolecularMagneticHamiltonian &fq_hamiltonian) const
Definition: GSpinorBasis.hpp:743
GSpinorBasis(const NuclearFramework &nuclear_framework, const std::string &basisset_name_alpha, const std::string &basisset_name_beta)
Definition: GSpinorBasis.hpp:201
typename Shell::BasisFunction BasisFunction
Definition: GSpinorBasis.hpp:80
auto quantize(const FQOneElectronOperator &fq_one_op) const -> enable_if_t< std::is_same< Z, GTOShell >::value, GSQOneElectronOperator< product_t< typename FQOneElectronOperator::Scalar, ExpansionScalar >, typename FQOneElectronOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:297
enable_if_t< std::is_same< S, double >::value, ScalarGSQOneElectronOperatorProduct< double > > quantize(const ElectronicSpinSquaredOperator &fq_S2_op) const
Definition: GSpinorBasis.hpp:839
static GSpinorBasis< ExpansionScalar, Shell > FromUnrestricted(const USpinOrbitalBasis< ExpansionScalar, Shell > &u_spinor_basis)
Definition: GSpinorBasis.hpp:249
_Shell Shell
Definition: GSpinorBasis.hpp:68
GMullikenDomain< ExpansionScalar > mullikenDomain(const std::function< bool(const Shell &)> &selector) const
Definition: GSpinorBasis.hpp:926
enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQHamiltonian< ExpansionScalar > > quantize(const FQMolecularPauliHamiltonian &fq_hamiltonian) const
Definition: GSpinorBasis.hpp:765
auto quantize(const ElectronicSpinOperator &fq_one_op) const -> enable_if_t< std::is_same< Z, GTOShell >::value, GSQOneElectronOperator< product_t< ElectronicSpinOperator::Scalar, ExpansionScalar >, ElectronicSpinOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:333
auto quantize(const SpinZeemanOperator &op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQOneElectronOperator< product_t< SpinZeemanOperator::Scalar, ExpansionScalar >, SpinZeemanOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:659
GSpinorBasis(const Molecule &molecule, const std::string &basisset_name)
Definition: GSpinorBasis.hpp:173
GSpinorBasis(const ScalarBasis< Shell > &scalar_basis)
Definition: GSpinorBasis.hpp:149
auto quantize(const FQOneElectronOperator &fq_one_op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQOneElectronOperator< product_t< typename FQOneElectronOperator::Scalar, ExpansionScalar >, typename FQOneElectronOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:495
GSpinorBasis(const ScalarBasis< Shell > &scalar_basis, const Transformation &C)
Definition: GSpinorBasis.hpp:129
const SpinResolved< ScalarBasis< Shell > > & scalarBases() const
Definition: GSpinorBasis.hpp:893
size_t numberOfCoefficients(const Spin &sigma) const
Definition: GSpinorBasis.hpp:266
GSpinorBasis(const Molecule &molecule, const std::string &basisset_name, const HomogeneousMagneticField &B, typename std::enable_if< std::is_same< Z, LondonGTOShell >::value >::type *=0)
Definition: GSpinorBasis.hpp:187
GSpinorBasis(const SpinResolved< ScalarBasis< Shell > > &scalar_bases, const Transformation &C)
Definition: GSpinorBasis.hpp:99
GMullikenDomain< ExpansionScalar > mullikenDomain(const std::function< bool(const BasisFunction &)> &selector) const
Definition: GSpinorBasis.hpp:909
auto quantize(const DiamagneticOperator &op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQOneElectronOperator< product_t< DiamagneticOperator::Scalar, ExpansionScalar >, DiamagneticOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:617
enable_if_t< std::is_same< S, complex >::value, ScalarGSQOneElectronOperatorProduct< complex > > quantize(const ElectronicSpinSquaredOperator &fq_S2_op) const
Definition: GSpinorBasis.hpp:813
auto quantize(const CoulombRepulsionOperator &coulomb_op) const -> enable_if_t< std::is_same< Z, GTOShell >::value, GSQTwoElectronOperator< product_t< CoulombRepulsionOperator::Scalar, ExpansionScalar >, CoulombRepulsionOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:424
auto quantize(const ElectronicSpinOperator &fq_one_op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQOneElectronOperator< product_t< ElectronicSpinOperator::Scalar, ExpansionScalar >, ElectronicSpinOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:546
auto quantize(const OrbitalZeemanOperator &op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQOneElectronOperator< product_t< OrbitalZeemanOperator::Scalar, ExpansionScalar >, OrbitalZeemanOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:600
typename Shell::Primitive Primitive
Definition: GSpinorBasis.hpp:77
_ExpansionScalar ExpansionScalar
Definition: GSpinorBasis.hpp:65
size_t numberOfSpinors() const
Definition: GSpinorBasis.hpp:276
GSpinorBasis(const ScalarBasis< Shell > &alpha_scalar_basis, const ScalarBasis< Shell > &beta_scalar_basis)
Definition: GSpinorBasis.hpp:139
GSQHamiltonian< ExpansionScalar > quantize(const FQMolecularHamiltonian &fq_hamiltonian) const
Definition: GSpinorBasis.hpp:792
static GSpinorBasis< ExpansionScalar, Shell > FromRestricted(const RSpinOrbitalBasis< ExpansionScalar, Shell > &r_spinor_basis)
Definition: GSpinorBasis.hpp:231
GSpinorBasis(const NuclearFramework &nuclear_framework, const std::string &basisset_name)
Definition: GSpinorBasis.hpp:161
auto quantize(const CoulombRepulsionOperator &coulomb_op) const -> enable_if_t< std::is_same< Z, LondonGTOShell >::value, GSQTwoElectronOperator< product_t< CoulombRepulsionOperator::Scalar, ExpansionScalar >, CoulombRepulsionOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:678
GSpinorBasis(const ScalarBasis< Shell > &alpha_scalar_basis, const ScalarBasis< Shell > &beta_scalar_basis, const Transformation &C)
Definition: GSpinorBasis.hpp:119
auto quantize(const ElectronicSpin_zOperator &fq_one_op) const -> enable_if_t< std::is_same< Z, GTOShell >::value, GSQOneElectronOperator< product_t< ElectronicSpin_zOperator::Scalar, ExpansionScalar >, ElectronicSpin_zOperator::Vectorizer > >
Definition: GSpinorBasis.hpp:386
Definition: GTransformation.hpp:43
static GTransformation< Scalar > FromUnrestricted(const UTransformation< Scalar > &u_transformation)
Definition: GTransformation.hpp:111
Definition: HomogeneousMagneticField.hpp:30
static Matrix< double > calculateLibintIntegrals(const FQOneElectronOperator &fq_one_op, const ScalarBasis< GTOShell > &left_scalar_basis, const ScalarBasis< GTOShell > &right_scalar_basis)
Definition: IntegralCalculator.hpp:181
static auto calculate(BaseOneElectronIntegralEngine< Shell, N, IntegralScalar > &engine, const ShellSet< Shell > &left_shell_set, const ShellSet< Shell > &right_shell_set) -> std::array< MatrixX< IntegralScalar >, N >
Definition: IntegralCalculator.hpp:61
Definition: JacobiRotation.hpp:33
Definition: Molecule.hpp:34
Definition: NuclearFramework.hpp:35
Definition: OrbitalZeemanOperator.hpp:35
Definition: OverlapOperator.hpp:31
Definition: RSpinOrbitalBasis.hpp:63
Definition: SQHamiltonian.hpp:54
Definition: ScalarBasis.hpp:41
Definition: GSQTwoElectronOperator.hpp:179
const ScalarBasis< Shell > & scalarBasis() const
Definition: SimpleSpinOrbitalBasis.hpp:133
Definition: SimpleSpinorBasis.hpp:55
const Transformation & expansion() const
Definition: SimpleSpinorBasis.hpp:98
size_t numberOfOrbitals() const
Definition: SimpleTransformation.hpp:153
const Of & beta() const
Definition: SpinResolvedBase.hpp:140
const Of & alpha() const
Definition: SpinResolvedBase.hpp:130
Definition: SpinResolved.hpp:34
Definition: SpinZeemanOperator.hpp:30
Definition: SquareMatrix.hpp:39
static Self Zero(const size_t dim)
Definition: SquareMatrix.hpp:289
Definition: SquareRankFourTensor.hpp:36
static Self Zero(const size_t dim)
Definition: SquareRankFourTensor.hpp:147
Definition: StorageArray.hpp:38
Definition: USpinOrbitalBasis.hpp:60
static USpinOrbitalBasis< ExpansionScalar, Shell > FromRestricted(const RSpinOrbitalBasis< ExpansionScalar, Shell > &r_spinor_basis)
Definition: USpinOrbitalBasis.hpp:222
UTransformation< ExpansionScalar > expansion() const
Definition: USpinOrbitalBasis.hpp:238
Definition: complex.hpp:57
Definition: BaseOneElectronIntegralBuffer.hpp:25
typename std::enable_if< B, T >::type enable_if_t
Definition: type_traits.hpp:37
decltype(std::declval< T >() *std::declval< U >()) product_t
Definition: aliases.hpp:35
@ xz
Definition: DyadicCartesianDirection.hpp:30
@ yy
Definition: DyadicCartesianDirection.hpp:32
@ xy
Definition: DyadicCartesianDirection.hpp:29
@ xx
Definition: DyadicCartesianDirection.hpp:28
@ yz
Definition: DyadicCartesianDirection.hpp:33
@ zz
Definition: DyadicCartesianDirection.hpp:36
@ z
Definition: CartesianDirection.hpp:30
@ x
Definition: CartesianDirection.hpp:28
@ y
Definition: CartesianDirection.hpp:29
Spin
Definition: Spin.hpp:27
@ beta
Definition: Spin.hpp:29
@ alpha
Definition: Spin.hpp:28
Definition: BasisTransformable.hpp:37
Definition: JacobiRotatable.hpp:37
_ExpansionScalar ExpansionScalar
Definition: GSpinorBasis.hpp:945
Definition: SimpleSpinorBasis.hpp:38