Conjugate Gradient solver for SPD problems, which can work in serial or parallel for testing use. Not suitable for large problems.
()
| 148 | |
| 149 | @pytest.fixture(scope="function", autouse=True) |
| 150 | def cg_solver(): |
| 151 | """Conjugate Gradient solver for SPD problems, which can work in |
| 152 | serial or parallel for testing use. Not suitable for large |
| 153 | problems. |
| 154 | """ |
| 155 | |
| 156 | def _cg(comm, A, b, x, maxit=500, rtol=None): |
| 157 | rtol2 = 10 * np.finfo(x.array.dtype).eps if rtol is None else rtol**2 |
| 158 | |
| 159 | def _global_dot(comm, v0, v1): |
| 160 | return comm.allreduce(np.vdot(v0, v1), MPI.SUM) |
| 161 | |
| 162 | A_op = A.to_scipy() |
| 163 | nr = A_op.shape[0] |
| 164 | assert nr == A.index_map(0).size_local * A.block_size[0] |
| 165 | |
| 166 | # Create larger ghosted vector based on matrix column space |
| 167 | # and get initial y = A.x |
| 168 | p = dolfinx_vector(A.index_map(1), bs=A.block_size[1], dtype=x.array.dtype) |
| 169 | p.array[:nr] = x.array[:nr] |
| 170 | p.scatter_forward() |
| 171 | y = A_op @ p.array |
| 172 | |
| 173 | # Copy residual to p |
| 174 | r = b.array[:nr] - y |
| 175 | p.array[:nr] = r |
| 176 | |
| 177 | # Iterations of CG |
| 178 | rnorm0 = _global_dot(comm, r, r) |
| 179 | rnorm = rnorm0 |
| 180 | k = 0 |
| 181 | while k < maxit: |
| 182 | k += 1 |
| 183 | p.scatter_forward() |
| 184 | y = A_op @ p.array |
| 185 | alpha = rnorm / _global_dot(comm, p.array[:nr], y) |
| 186 | x.array[:nr] += alpha * p.array[:nr] |
| 187 | r -= alpha * y |
| 188 | rnorm_new = _global_dot(comm, r, r) |
| 189 | beta = rnorm_new / rnorm |
| 190 | rnorm = rnorm_new |
| 191 | if rnorm / rnorm0 < rtol2: |
| 192 | x.scatter_forward() |
| 193 | return |
| 194 | p.array[:nr] = beta * p.array[:nr] + r |
| 195 | |
| 196 | raise RuntimeError( |
| 197 | f"Solver exceeded max iterations ({maxit}). Relative residual={rnorm / rnorm0}." |
| 198 | ) |
| 199 | |
| 200 | return _cg |
no outgoing calls
no test coverage detected