diff --git a/opencl_fdfd/csr.py b/opencl_fdfd/csr.py index 3ecbac9..9761fc0 100644 --- a/opencl_fdfd/csr.py +++ b/opencl_fdfd/csr.py @@ -16,6 +16,7 @@ satisfy the constraints for the 'conjugate gradient' algorithm from typing import Dict, Any import time +import logging import numpy from numpy.linalg import norm @@ -27,6 +28,11 @@ import fdfd_tools.solvers from . import ops +__author__ = 'Jan Petykiewicz' + +logger = logging.getLogger(__name__) + + class CSRMatrix(object): """ Matrix stored in Compressed Sparse Row format, in GPU RAM. @@ -49,7 +55,6 @@ def cg(A: 'scipy.sparse.csr_matrix', err_threshold: float = 1e-6, context: pyopencl.Context = None, queue: pyopencl.CommandQueue = None, - verbose: bool = False, ) -> numpy.ndarray: """ General conjugate-gradient solver for sparse matrices, where A @ x = b. @@ -60,7 +65,6 @@ def cg(A: 'scipy.sparse.csr_matrix', :param err_threshold: Error threshold for successful solve, relative to norm(b) :param context: PyOpenCL context. Will be created if not given. :param queue: PyOpenCL command queue. Will be created if not given. - :param verbose: Whether to print statistics to screen. :return: Solution vector x; returned even if solve doesn't converge. """ @@ -102,13 +106,11 @@ def cg(A: 'scipy.sparse.csr_matrix', _, err2 = rhoerr_step(r, []) b_norm = numpy.sqrt(err2) - if verbose: - print('b_norm check: ', b_norm) + logging.debug('b_norm check: ', b_norm) success = False for k in range(max_iters): - if verbose: - print('[{:06d}] rho {:.4} alpha {:4.4}'.format(k, rho, alpha), end=' ') + logging.debug('[{:06d}] rho {:.4} alpha {:4.4}'.format(k, rho, alpha)) rho_prev = rho e = xr_step(x, p, r, v, alpha, []) @@ -116,8 +118,7 @@ def cg(A: 'scipy.sparse.csr_matrix', errs += [numpy.sqrt(err2) / b_norm] - if verbose: - print('err', errs[-1]) + logging.debug('err {}'.format(errs[-1])) if errs[-1] < err_threshold: success = True @@ -128,7 +129,7 @@ def cg(A: 'scipy.sparse.csr_matrix', alpha = rho / dot(p, v, e) if verbose and k % 1000 == 0: - print(k) + logging.info('iteration {}'.format(k)) ''' Done solving @@ -137,17 +138,16 @@ def cg(A: 'scipy.sparse.csr_matrix', x = x.get() - if verbose: - if success: - print('Success', end='') - else: - print('Failure', end=', ') - print(', {} iterations in {} sec: {} iterations/sec \ - '.format(k, time_elapsed, k / time_elapsed)) - print('final error', errs[-1]) - print('overhead {} sec'.format(start_time2 - start_time)) + if success: + logging.info('Solve success') + else: + logging.warning('Solve failure') + logging.info('{} iterations in {} sec: {} iterations/sec \ + '.format(k, time_elapsed, k / time_elapsed)) + logging.debug('final error {}'.format(errs[-1])) + logging.debug('overhead {} sec'.format(start_time2 - start_time)) - print('Final residual:', norm(A @ x - b) / norm(b)) + logging.info('Final residual: {}'.format(norm(A @ x - b) / norm(b))) return x diff --git a/opencl_fdfd/main.py b/opencl_fdfd/main.py index 3b79aa9..a48aec0 100644 --- a/opencl_fdfd/main.py +++ b/opencl_fdfd/main.py @@ -8,6 +8,7 @@ a matrix). from typing import List import time +import logging import numpy from numpy.linalg import norm @@ -18,8 +19,11 @@ import fdfd_tools.operators from . import ops + __author__ = 'Jan Petykiewicz' +logger = logging.getLogger(__name__) + def cg_solver(omega: complex, dxes: List[List[numpy.ndarray]], @@ -32,7 +36,6 @@ def cg_solver(omega: complex, max_iters: int = 40000, err_threshold: float = 1e-6, context: pyopencl.Context = None, - verbose: bool = False, ) -> numpy.ndarray: """ OpenCL FDFD solver using the iterative conjugate gradient (cg) method @@ -57,7 +60,6 @@ def cg_solver(omega: complex, :param err_threshold: If (r @ r.conj()) / norm(1j * omega * J) < err_threshold, success. Default 1e-6. :param context: PyOpenCL context to run in. If not given, construct a new context. - :param verbose: If True, print progress to stdout. Default False. :return: E-field which solves the system. Returned even if we did not converge. """ @@ -171,12 +173,13 @@ def cg_solver(omega: complex, _, err2 = rhoerr_step(r, []) b_norm = numpy.sqrt(err2) - print('b_norm check: ', b_norm) + logging.debug('b_norm check: {}'.format(b_norm)) success = False for k in range(max_iters): - if verbose: - print('[{:06d}] rho {:.4} alpha {:4.4}'.format(k, rho, alpha), end=' ') + do_print = (k % 100 == 0) + if do_print: + logger.debug('[{:06d}] rho {:.4} alpha {:4.4}'.format(k, rho, alpha)) rho_prev = rho e = xr_step(x, p, r, v, alpha, []) @@ -184,8 +187,8 @@ def cg_solver(omega: complex, errs += [numpy.sqrt(err2) / b_norm] - if verbose: - print('err', errs[-1]) + if do_print: + logger.debug('err {}'.format(errs[-1])) if errs[-1] < err_threshold: success = True @@ -196,7 +199,7 @@ def cg_solver(omega: complex, alpha = rho / dot(p, v, e) if k % 1000 == 0: - print(k) + logger.info('iteration {}'.format(k)) ''' Done solving @@ -210,18 +213,18 @@ def cg_solver(omega: complex, x = (Pr * x).get() if success: - print('Success', end='') + logger.info('Solve success') else: - print('Failure', end=', ') - print(', {} iterations in {} sec: {} iterations/sec \ + logger.warning('Solve failure') + logger.info('{} iterations in {} sec: {} iterations/sec \ '.format(k, time_elapsed, k / time_elapsed)) - print('final error', errs[-1]) - print('overhead {} sec'.format(start_time2 - start_time)) + logger.debug('final error {}'.format(errs[-1])) + logger.debug('overhead {} sec'.format(start_time2 - start_time)) A0 = fdfd_tools.operators.e_full(omega, dxes, epsilon, mu).tocsr() if adjoint: # Remember we conjugated all the contents of A earlier A0 = A0.T - print('Post-everything residual:', norm(A0 @ x - b) / norm(b)) + logger.info('Post-everything residual: {}'.format(norm(A0 @ x - b) / norm(b))) return x diff --git a/opencl_fdfd/ops.py b/opencl_fdfd/ops.py index 4f81420..2e0f160 100644 --- a/opencl_fdfd/ops.py +++ b/opencl_fdfd/ops.py @@ -8,6 +8,7 @@ See kernels/ for any of the .cl files loaded in this file. """ from typing import List, Callable +import logging import numpy import jinja2 @@ -18,6 +19,8 @@ from pyopencl.elementwise import ElementwiseKernel from pyopencl.reduction import ReductionKernel +logger = logging.getLogger(__name__) + # Create jinja2 env on module load jinja_env = jinja2.Environment(loader=jinja2.PackageLoader(__name__, 'kernels')) @@ -145,6 +148,11 @@ def create_a(context: pyopencl.Context, e2 = H2E_kernel(E, H, oeps, Pl, pec, *idxes[1], wait_for=[e2]) return [e2] + logger.debug('Preamble: \n{}'.format(preamble)) + logger.debug('p2e: \n{}'.format(p2e_source)) + logger.debug('e2h: \n{}'.format(e2h_source)) + logger.debug('h2e: \n{}'.format(h2e_source)) + return spmv