diff --git a/fig8.py b/fig8.py index f97ef9b4..747c7913 100644 --- a/fig8.py +++ b/fig8.py @@ -7,6 +7,8 @@ import ipdb os.environ["OMP_NUM_THREADS"] = "1" fire.parameters["loopy"] = {"silenced_warnings": ["v1_scheduler_fallback"]} +import warnings +warnings.filterwarnings("ignore", category=RuntimeWarning) def test_eikonal_values_fig8(): @@ -19,7 +21,7 @@ def test_eikonal_values_fig8(): # (MLT/spectral_quadrilateral/DG_triangle/DG_quadrilateral) # You can either specify a cell_type+variant or a method # accepted_variants = ["lumped", "equispaced", "DG"] - "degree": 4, # p order + "degree": 2, # p order p=4 ok "dimension": 2, # dimension } @@ -47,7 +49,7 @@ def test_eikonal_values_fig8(): # the helper function `create_transect`. dictionary["acquisition"] = { "source_type": "ricker", - # "source_locations": [(-0.5, 0.2), (-0.5, 0.25), (-0.5, 0.3)], + # "source_locations": [(-0.5, 0.25), (-0.5, 0.35), (-0.5, 0.5)], "source_locations": [(-0.5, 0.25)], "frequency": 5.0, "delay": 1.5, @@ -158,18 +160,118 @@ def assemble_eik(Wave, u, vy, dx): ''' Eikonal with stabilizer term ''' - eps = fire.Constant(1.) * fire.CellDiameter(Wave.mesh) # Stabilizer - - # delta = fire.Constant(float_info.min) - delta = fire.Constant(float_info.epsilon) - f = fire.Constant(1.0) + eps = fire.Constant(1.0) * fire.CellDiameter(Wave.mesh) # Stabilizer + delta = fire.Constant(float_info.min) + # delta = fire.Constant(float_info.epsilon) grad_u_norm = fire.sqrt(fire.inner(fire.grad(u), fire.grad(u))) + delta + f = fire.Constant(1.0) F = (grad_u_norm * vy * dx - f / Wave.c * vy * dx + eps * fire.inner( fire.grad(u), fire.grad(vy)) * dx) return F -def solve_eik(Wave, bcs_eik): +def solve_prop(nl_solver='newtonls', l_solver='preonly', + user_rtol=1e-16, user_iter=50, monitor=False): + ''' + Solver Parameters + https://petsc.org/release/manualpages/SNES/SNESType/ + https://petsc.org/release/manualpages/KSP/KSPType/ + https://petsc.org/release/manualpages/PC/PCType/ + ''' + + # Tolerances and iterations + user_atol = user_rtol**2 + user_stol = user_rtol**2.5 + ksp_max_it = user_iter + + param_solver = {'snes_type': nl_solver, 'ksp_type': l_solver} + + if nl_solver == 'newtontr': # newton, cauchy, dogleg + param_solver.update({'snes_tr_fallback_type': 'newton'}) + + if nl_solver == 'ngmres': # difference, none, linesearch + param_solver.update({'snes_ngmres_select_type': 'linesearch'}) + + if nl_solver == 'qn': + param_solver.update({'snes_qn_m_type': 5, + 'snes_qn_powell_descent': True, + # lbfgs, broyden, badbroyden + 'snes_qn_type': 'badbroyden', + # diagonal, none, scalar, jacobian + 'snes_qn_scale_type': 'jacobian'}) + + if nl_solver == 'ngs': + param_solver.update({'snes_ngs_sweeps': 2, + 'snes_ngs_atol': user_rtol, + 'snes_ngs_rtol': user_atol, + 'snes_ngs_stol': user_stol, + 'snes_ngs_max_it': user_iter}) + + if nl_solver == 'ncg': + # fr, prp, dy, hs, cd + param_solver.update({'snes_ncg_type': 'cd'}) + + if nl_solver == 'anderson': + param_solver.update({'snes_anderson_m': 3, + 'snes_anderson_beta': 0.3}) + + if l_solver == 'preonly': + ig_nz = False + pc_type = 'lu' # lu, cholesky + + param_solver.update({'pc_factor_mat_solver_type': 'umfpack'}) # mumps + else: + ig_nz = True + pc_type = 'icc' # ilu, icc + + if l_solver == 'gmres': + param_solver.update({'ksp_gmres_restart': 3, + 'ksp_gmres_haptol': user_stol}) + + param_solver.update({ + 'snes_linesearch_type': 'l2', # l2, cp, basic + 'snes_linesearch_damping': 0.25, + 'snes_linesearch_maxstep': 1.0, + 'snes_max_funcs': 1000, + 'snes_linesearch_order': 3, + 'snes_linesearch_alpha': 0.5, + 'snes_max_it': user_iter, + 'snes_linesearch_rtol': user_rtol, + 'snes_linesearch_atol': user_atol, + 'snes_rtol': user_atol, + 'snes_atol': user_rtol, + 'snes_stol': user_stol, + 'ksp_max_it': ksp_max_it, + 'ksp_rtol': user_rtol, + 'ksp_atol': user_atol, + 'ksp_initial_guess_nonzero': ig_nz, + 'pc_type': pc_type, + 'pc_factor_reuse_ordering': True, + 'snes_monitor': None, + }) + + if monitor: # For debugging + param_solver.update({ + 'snes_view': None, + 'snes_converged_reason': None, + 'snes_linesearch_monitor': None, + 'ksp_monitor_true_residual': None, + 'ksp_converged_reason': None, + 'report': True, + 'error_on_nonconvergence': True}) + return param_solver + + +def clean_inst_num(data_arr): + '''' + Clean data: Set NaNs and negative values to zero + ''' + data_arr[np.where(np.isnan(data_arr) | np.isinf( + data_arr) | (data_arr < 0.0))] = 0.0 + return data_arr + + +def solve_eik(Wave, bcs_eik, tol=1e-16): ''' Solve nonlinear eikonal ''' @@ -182,27 +284,61 @@ def solve_eik(Wave, bcs_eik): # Linear Eikonal print('Solving Pre-Eikonal') FeikL = linear_eik(Wave, u, vy, fire.dx) - fire.solve(fire.lhs(FeikL) == fire.rhs(FeikL), yp, bcs=bcs_eik) + J = fire.derivative(FeikL, yp) + + # Initial guess + cell_diameter_function = fire.Function(Wave.function_space) + cell_diameter_function.interpolate(fire.CellDiameter(Wave.mesh)) + yp.assign(cell_diameter_function.dat.data_with_halos.max() + / Wave.c.dat.data_with_halos.min()) + + # Linear Eikonal + user_rtol = tol**0.5 + # newtontr, nrichardson, qn, ngs, ncg, ngmres, anderson + nl_solver = 'newtontr' + l_solver = 'gmres' # gmres, bcgs, preonly + while True: + try: + pL = solve_prop(nl_solver=nl_solver, l_solver=l_solver, + user_rtol=user_rtol, user_iter=50) + fire.solve(fire.lhs(FeikL) == fire.rhs(FeikL), yp, + bcs=bcs_eik, solver_parameters=pL, J=J) + print(f"\nSolver Executed Successfully. Tol: {user_rtol:.1e}") + break + except Exception as e: + print(f"Error Solving: {e}") + user_rtol = user_rtol * 10 if user_rtol < 1e-3 \ + else round(user_rtol + 1e-3, 3) + if user_rtol > 1e-2: + print("\nTolerance too high. Exiting.") + break + + # Clean data: Set NaNs and negative values to zero + yp.dat.data_with_halos[:] = clean_inst_num(yp.dat.data_with_halos) # Nonlinear Eikonal print('Solving Post-Eikonal') Feik = assemble_eik(Wave, yp, vy, fire.dx) J = fire.derivative(Feik, yp) - user_tol = 1e-16 - fire.solve(Feik == 0, yp, bcs=bcs_eik, solver_parameters={ - 'snes_type': 'vinewtonssls', - 'snes_max_it': 1000, - 'snes_atol': user_tol, # Increase the tolerance - 'snes_rtol': 1e-20, - 'snes_linesearch_type': 'l2', - 'snes_linesearch_damping': 1.00, - 'snes_linesearch_maxstep': 0.50, - 'snes_linesearch_order': 2, - 'pc_type': 'lu', - 'ksp_type': 'gmres', - 'ksp_max_it': 1000, - 'ksp_atol': user_tol, # Increase the tolerance - }, J=J) + user_rtol = tol + # newtonls, newtontr, nrichardson, qn, ngs, ncg, ngmres, anderson + nl_solver = 'newtonls' + l_solver = 'preonly' # gmres, bcgs, preonly + while True: + try: + pNL = solve_prop(nl_solver=nl_solver, l_solver=l_solver, + user_rtol=user_rtol, user_iter=50) + fire.solve(Feik == 0, yp, bcs=bcs_eik, solver_parameters=pNL, J=J) + print(f"\nSolver Executed Successfully. Tol: {user_rtol:.1e}") + break + except Exception as e: + print(f"Error Solving: {e}") + user_rtol = user_rtol * 10 if user_rtol < 1e-3 \ + else round(user_rtol + 1e-3, 3) + if user_rtol > 1e-2: + print('\nHigh Tolerance. Exiting!') + break + # yp.dat.data_with_halos[:] = clean_inst_num(yp.dat.data_with_halos) return yp diff --git a/output/Eik/Eik_0.vtu b/output/Eik/Eik_0.vtu index 87bcf8c0..a82502df 100644 Binary files a/output/Eik/Eik_0.vtu and b/output/Eik/Eik_0.vtu differ