Skip to content

Commit 4b142ea

Browse files
author
Roberto Di Remigio
committed
Do not calculate inverse of T explicitly in IEFSolver
1 parent a8b52f5 commit 4b142ea

File tree

4 files changed

+56
-183
lines changed

4 files changed

+56
-183
lines changed

CHANGELOG.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@
77
- The `PEDRA.OUT` cavity generator log now reports the initial _and_ final
88
lists of spheres. The final list only contains those spheres that were
99
actually tesselated and, possibly, the added spheres.
10+
- For all solvers, the symmetrization needed to obtain the polarization weights
11+
happens directly on the computed charges, instead of symmetrizing the system
12+
matrices.
13+
- The `IEFSolver` object now stores the unsymmetrized T and R matrices.
14+
The explicit calculation of the inverse of T is thus avoided, the drawback is
15+
that two square matrices of size equal to the cavity size are stored instead
16+
of just one. A [partially pivoted LU decomposition](http://eigen.tuxfamily.org/dox/classEigen_1_1PartialPivLU.html)
17+
is used to solve the linear system.
18+
In the isotropic case no inverses are ever explicitly calculated. In the
19+
anisotropic case, the inverse of the internal S matrix is still needed to
20+
form R.
1021
- The `CPCMSolver` object now stores the scaled, unsymmetrized S matrix. The
11-
explicit calculation and storage of its inverse is thus avoided. A robust
12-
Cholesky decomposition is used to solve the linear equation system. The
13-
symmetrization needed to obtain the polarization weights happens directly on
14-
the computed charges.
22+
explicit calculation and storage of its inverse is thus avoided.
23+
A [robust Cholesky decomposition](http://eigen.tuxfamily.org/dox/classEigen_1_1LDLT.html) is
24+
used to solve the linear equation system.
25+
The symmetrization needed to obtain the polarization weights happens directly
26+
on the computed charges.
1527
- The `hermitivitize` function can now correctly symmetrize vectors.
1628

1729
### Fixed
@@ -25,6 +37,8 @@
2537
### Removed
2638

2739
- The function `CPCMMatrix` in the `SolverImpl.hpp` header file is no longer available.
40+
- The functions `anisotropicIEFMatrix` and `isotropicIEFMatrix` in the
41+
`SolverImpl.hpp` header file are no longer available.
2842

2943
## [v1.1.2] (2016-05-31)
3044

src/solver/IEFSolver.cpp

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,36 +50,34 @@ void IEFSolver::buildSystemMatrix_impl(const Cavity & cavity, const IGreensFunct
5050

5151
void IEFSolver::buildAnisotropicMatrix(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
5252
{
53-
fullPCMMatrix_ = anisotropicIEFMatrix(cav, gf_i, gf_o);
54-
// Symmetrize K := (K + K+)/2
55-
if (hermitivitize_) {
56-
hermitivitize(fullPCMMatrix_);
57-
}
53+
Tepsilon_ = anisotropicTEpsilon(cav, gf_i, gf_o);
5854
// Pack into a block diagonal matrix
5955
// The number of irreps in the group
6056
int nrBlocks = cav.pointGroup().nrIrrep();
6157
// The size of the irreducible portion of the cavity
6258
int dimBlock = cav.irreducible_size();
6359
// For the moment just packs into a std::vector<Eigen::MatrixXd>
64-
symmetryPacking(blockPCMMatrix_, fullPCMMatrix_, dimBlock, nrBlocks);
60+
symmetryPacking(blockTepsilon_, Tepsilon_, dimBlock, nrBlocks);
61+
62+
Rinfinity_ = anisotropicRinfinity(cav, gf_i, gf_o);
63+
symmetryPacking(blockRinfinity_, Rinfinity_, dimBlock, nrBlocks);
6564

6665
built_ = true;
6766
}
6867

6968
void IEFSolver::buildIsotropicMatrix(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
7069
{
71-
fullPCMMatrix_ = isotropicIEFMatrix(cav, gf_i, profiles::epsilon(gf_o.permittivity()));
72-
// Symmetrize K := (K + K+)/2
73-
if (hermitivitize_) {
74-
hermitivitize(fullPCMMatrix_);
75-
}
70+
Tepsilon_ = isotropicTEpsilon(cav, gf_i, profiles::epsilon(gf_o.permittivity()));
7671
// Pack into a block diagonal matrix
7772
// The number of irreps in the group
7873
int nrBlocks = cav.pointGroup().nrIrrep();
7974
// The size of the irreducible portion of the cavity
8075
int dimBlock = cav.irreducible_size();
8176
// For the moment just packs into a std::vector<Eigen::MatrixXd>
82-
symmetryPacking(blockPCMMatrix_, fullPCMMatrix_, dimBlock, nrBlocks);
77+
symmetryPacking(blockTepsilon_, Tepsilon_, dimBlock, nrBlocks);
78+
79+
Rinfinity_ = isotropicRinfinity(cav, gf_i);
80+
symmetryPacking(blockRinfinity_, Rinfinity_, dimBlock, nrBlocks);
8381

8482
built_ = true;
8583
}
@@ -89,12 +87,15 @@ Eigen::VectorXd IEFSolver::computeCharge_impl(const Eigen::VectorXd & potential,
8987
// The potential and charge vector are of dimension equal to the
9088
// full dimension of the cavity. We have to select just the part
9189
// relative to the irrep needed.
92-
int fullDim = fullPCMMatrix_.rows();
90+
int fullDim = Rinfinity_.rows();
9391
Eigen::VectorXd charge = Eigen::VectorXd::Zero(fullDim);
94-
int nrBlocks = blockPCMMatrix_.size();
92+
int nrBlocks = blockRinfinity_.size();
9593
int irrDim = fullDim/nrBlocks;
9694
charge.segment(irrep*irrDim, irrDim) =
97-
- blockPCMMatrix_[irrep] * potential.segment(irrep*irrDim, irrDim);
95+
- blockTepsilon_[irrep].partialPivLu().solve(blockRinfinity_[irrep] * potential.segment(irrep*irrDim, irrDim));
96+
// Symmetrize ASC charge := (charge + charge*)/2
97+
if (hermitivitize_) hermitivitize(charge);
98+
9899
return charge;
99100
}
100101

src/solver/IEFSolver.hpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22
/*
33
* PCMSolver, an API for the Polarizable Continuum Model
44
* Copyright (C) 2013-2015 Roberto Di Remigio, Luca Frediani and contributors
5-
*
5+
*
66
* This file is part of PCMSolver.
7-
*
7+
*
88
* PCMSolver is free software: you can redistribute it and/or modify
99
* it under the terms of the GNU Lesser General Public License as published by
1010
* the Free Software Foundation, either version 3 of the License, or
1111
* (at your option) any later version.
12-
*
12+
*
1313
* PCMSolver is distributed in the hope that it will be useful,
1414
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1515
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1616
* GNU Lesser General Public License for more details.
17-
*
17+
*
1818
* You should have received a copy of the GNU Lesser General Public License
1919
* along with PCMSolver. If not, see <http://www.gnu.org/licenses/>.
20-
*
20+
*
2121
* For information on the complete list of contributors to the
2222
* PCMSolver API, see: <http://pcmsolver.readthedocs.org/>
2323
*/
@@ -43,7 +43,13 @@ class IGreensFunction;
4343
* \class IEFSolver
4444
* \brief IEFPCM, collocation-based solver
4545
* \author Luca Frediani, Roberto Di Remigio
46-
* \date 2011, 2015
46+
* \date 2011, 2015, 2016
47+
*
48+
* \note We store the unsymmetrized T(epsilon) and Rinfinity matrices.
49+
* The ASC is obtained by multiplying the MEP by Rinfinity and then using a partially
50+
* pivoted LU decomposition of T(epsilon) on the resulting vector.
51+
* The ASC is then symmetrized. This avoids computing and storing the inverse
52+
* explicitly.
4753
*/
4854

4955
class IEFSolver : public PCMSolver
@@ -73,10 +79,14 @@ class IEFSolver : public PCMSolver
7379
private:
7480
/*! Whether the system matrix has to be symmetrized */
7581
bool hermitivitize_;
76-
/*! PCM matrix, not symmetry blocked */
77-
Eigen::MatrixXd fullPCMMatrix_;
78-
/*! PCM matrix, symmetry blocked form */
79-
std::vector<Eigen::MatrixXd> blockPCMMatrix_;
82+
/*! T(epsilon) matrix, not symmetry blocked */
83+
Eigen::MatrixXd Tepsilon_;
84+
/*! T(epsilon) matrix, symmetry blocked form */
85+
std::vector<Eigen::MatrixXd> blockTepsilon_;
86+
/*! R_infinity matrix, not symmetry blocked */
87+
Eigen::MatrixXd Rinfinity_;
88+
/*! R_infinity matrix, symmetry blocked form */
89+
std::vector<Eigen::MatrixXd> blockRinfinity_;
8090

8191
/*! \brief Calculation of the PCM matrix
8292
* \param[in] cavity the cavity to be used

src/solver/SolverImpl.hpp

Lines changed: 2 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -41,157 +41,6 @@
4141
* \date 2015
4242
*/
4343

44-
/*! \brief Builds the **anisotropic** IEFPCM matrix
45-
* \param[in] cav the discretized cavity
46-
* \param[in] gf_i Green's function inside the cavity
47-
* \param[in] gf_o Green's function outside the cavity
48-
* \return the \f$ \mathbf{K} = \mathbf{T}^{-1}\mathbf{R}\mathbf{A} \f$ matrix
49-
*
50-
* This function calculates the PCM matrix. We use the following definitions:
51-
* \f[
52-
* \begin{align}
53-
* \mathbf{T} &=
54-
* \left(2\pi\mathbf{I} - \mathbf{D}_\mathrm{e}\mathbf{A}\right)\mathbf{S}_\mathrm{i}
55-
* +\mathbf{S}_\mathrm{e}\left(2\pi\mathbf{I} +
56-
* \mathbf{A}\mathbf{D}_\mathrm{i}^\dagger\right) \\
57-
* \mathbf{R} &=
58-
* \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{e}\right) -
59-
* \mathbf{S}_\mathrm{e}\mathbf{S}^{-1}_\mathrm{i}\left(2\pi\mathbf{A}^{-1}-\mathbf{D}_\mathrm{i}\right)
60-
* \end{align}
61-
* \f]
62-
* The matrix is not symmetrized and is not symmetry packed.
63-
*/
64-
inline Eigen::MatrixXd anisotropicIEFMatrix(const Cavity & cav, const IGreensFunction & gf_i, const IGreensFunction & gf_o)
65-
{
66-
// The total size of the cavity
67-
PCMSolverIndex cavitySize = cav.size();
68-
// The number of irreps in the group
69-
int nrBlocks = cav.pointGroup().nrIrrep();
70-
// The size of the irreducible portion of the cavity
71-
int dimBlock = cav.irreducible_size();
72-
73-
// Compute SI, DI and SE, DE on the whole cavity, regardless of symmetry
74-
TIMER_ON("Computing SI");
75-
Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
76-
TIMER_OFF("Computing SI");
77-
TIMER_ON("Computing DI");
78-
Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
79-
TIMER_OFF("Computing DI");
80-
TIMER_ON("Computing SE");
81-
Eigen::MatrixXd SE = gf_o.singleLayer(cav.elements());
82-
TIMER_OFF("Computing SE");
83-
TIMER_ON("Computing DE");
84-
Eigen::MatrixXd DE = gf_o.doubleLayer(cav.elements());
85-
TIMER_OFF("Computing DE");
86-
87-
// Perform symmetry blocking
88-
// If the group is C1 avoid symmetry blocking, we will just pack the fullPCMMatrix
89-
// into "block diagonal" when all other manipulations are done.
90-
if (cav.pointGroup().nrGenerators() != 0) {
91-
TIMER_ON("Symmetry blocking");
92-
symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
93-
symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
94-
symmetryBlocking(DE, cavitySize, dimBlock, nrBlocks);
95-
symmetryBlocking(SE, cavitySize, dimBlock, nrBlocks);
96-
TIMER_OFF("Symmetry blocking");
97-
}
98-
99-
Eigen::MatrixXd a = cav.elementArea().asDiagonal();
100-
Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);
101-
102-
// 1. Form T
103-
TIMER_ON("Assemble T matrix");
104-
Eigen::MatrixXd fullPCMMatrix = ((2 * M_PI * Id - DE * a) * SI + SE * (2 * M_PI * Id + a * DI.adjoint().eval()));
105-
TIMER_OFF("Assemble T matrix");
106-
// 2. Invert T using LU decomposition with full pivoting
107-
// This is a rank-revealing LU decomposition, this allows us
108-
// to test if T is invertible before attempting to invert it.
109-
TIMER_ON("Invert T matrix");
110-
Eigen::FullPivLU<Eigen::MatrixXd> T_LU(fullPCMMatrix);
111-
if (!(T_LU.isInvertible())) PCMSOLVER_ERROR("T matrix is not invertible!", BOOST_CURRENT_FUNCTION);
112-
fullPCMMatrix = T_LU.inverse();
113-
TIMER_OFF("Invert T matrix");
114-
Eigen::FullPivLU<Eigen::MatrixXd> SI_LU(SI);
115-
if (!(SI_LU.isInvertible())) PCMSOLVER_ERROR("SI matrix is not invertible!", BOOST_CURRENT_FUNCTION);
116-
TIMER_ON("Assemble T^-1R matrix");
117-
fullPCMMatrix *= ((2 * M_PI * Id - DE * a) - SE * SI_LU.inverse() * (2 * M_PI * Id - DI * a));
118-
TIMER_OFF("Assemble T^-1R matrix");
119-
120-
return fullPCMMatrix;
121-
}
122-
123-
/*! \brief Builds the **isotropic** IEFPCM matrix
124-
* \param[in] cav the discretized cavity
125-
* \param[in] gf_i Green's function inside the cavity
126-
* \param[in] epsilon permittivity outside the cavity
127-
* \return the \f$ \mathbf{K} = \mathbf{T}^{-1}\mathbf{R}\mathbf{A} \f$ matrix
128-
*
129-
* This function calculates the PCM matrix. We use the following definitions:
130-
* \f[
131-
* \begin{align}
132-
* \mathbf{T} &=
133-
* \left(2\pi\frac{\varepsilon+1}{\varepsilon-1}\mathbf{I} - \mathbf{D}_\mathrm{i}\mathbf{A}\right)\mathbf{S}_\mathrm{i} \\
134-
* \mathbf{R} &=
135-
* \left(2\pi\mathbf{A}^{-1} - \mathbf{D}_\mathrm{i}\right)
136-
* \end{align}
137-
* \f]
138-
* The matrix is not symmetrized and is not symmetry packed.
139-
*/
140-
inline Eigen::MatrixXd isotropicIEFMatrix(const Cavity & cav, const IGreensFunction & gf_i, double epsilon)
141-
{
142-
// The total size of the cavity
143-
PCMSolverIndex cavitySize = cav.size();
144-
// The number of irreps in the group
145-
int nrBlocks = cav.pointGroup().nrIrrep();
146-
// The size of the irreducible portion of the cavity
147-
int dimBlock = cav.irreducible_size();
148-
149-
// Compute SI and DI on the whole cavity, regardless of symmetry
150-
TIMER_ON("Computing SI");
151-
Eigen::MatrixXd SI = gf_i.singleLayer(cav.elements());
152-
TIMER_OFF("Computing SI");
153-
TIMER_ON("Computing DI");
154-
Eigen::MatrixXd DI = gf_i.doubleLayer(cav.elements());
155-
TIMER_OFF("Computing DI");
156-
157-
// Perform symmetry blocking
158-
// If the group is C1 avoid symmetry blocking, we will just pack the fullPCMMatrix
159-
// into "block diagonal" when all other manipulations are done.
160-
if (cav.pointGroup().nrGenerators() != 0) {
161-
TIMER_ON("Symmetry blocking");
162-
symmetryBlocking(DI, cavitySize, dimBlock, nrBlocks);
163-
symmetryBlocking(SI, cavitySize, dimBlock, nrBlocks);
164-
TIMER_OFF("Symmetry blocking");
165-
}
166-
167-
Eigen::MatrixXd a = cav.elementArea().asDiagonal();
168-
Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);
169-
170-
// Tq = -Rv -> q = -(T^-1 * R)v = -Kv
171-
// T = (2 * M_PI * fact * aInv - DI) * a * SI; R = (2 * M_PI * aInv - DI)
172-
// fullPCMMatrix_ = K = T^-1 * R * a
173-
// 1. Form T
174-
double fact = (epsilon + 1.0)/(epsilon - 1.0);
175-
TIMER_ON("Assemble T matrix");
176-
Eigen::MatrixXd fullPCMMatrix = (2 * M_PI * fact * Id - DI * a) * SI;
177-
TIMER_OFF("Assemble T matrix");
178-
// 2. Invert T using LU decomposition with full pivoting
179-
// This is a rank-revealing LU decomposition, this allows us
180-
// to test if T is invertible before attempting to invert it.
181-
TIMER_ON("Invert T matrix");
182-
Eigen::FullPivLU<Eigen::MatrixXd> T_LU(fullPCMMatrix);
183-
if (!(T_LU.isInvertible()))
184-
PCMSOLVER_ERROR("T matrix is not invertible!", BOOST_CURRENT_FUNCTION);
185-
fullPCMMatrix = T_LU.inverse();
186-
TIMER_OFF("Invert T matrix");
187-
// 3. Multiply T^-1 and R
188-
TIMER_ON("Assemble T^-1R matrix");
189-
fullPCMMatrix *= (2 * M_PI * Id - DI * a);
190-
TIMER_OFF("Assemble T^-1R matrix");
191-
192-
return fullPCMMatrix;
193-
}
194-
19544
/*! \brief Builds the **anisotropic** \f$ \mathbf{T}_\varepsilon \f$ matrix
19645
* \param[in] cav the discretized cavity
19746
* \param[in] gf_i Green's function inside the cavity
@@ -322,9 +171,8 @@ inline Eigen::MatrixXd anisotropicRinfinity(const Cavity & cav, const IGreensFun
322171
Eigen::MatrixXd a = cav.elementArea().asDiagonal();
323172
Eigen::MatrixXd Id = Eigen::MatrixXd::Identity(cavitySize, cavitySize);
324173

325-
// Form T
326-
Eigen::FullPivLU<Eigen::MatrixXd> SI_LU(SI);
327-
if (!(SI_LU.isInvertible())) PCMSOLVER_ERROR("SI matrix is not invertible!", BOOST_CURRENT_FUNCTION);
174+
// Form R
175+
Eigen::PartialPivLU<Eigen::MatrixXd> SI_LU(SI);
328176
return ((2 * M_PI * Id - DE * a) - SE * SI_LU.inverse() * (2 * M_PI * Id - DI * a));
329177
}
330178

0 commit comments

Comments
 (0)