@@ -1448,8 +1448,8 @@ def test_logging(self, log_level):
14481448 tols = entry0 .KSPGetTolerances
14491449 assert tols ['rtol' ] == linear_solve_defaults ['ksp_rtol' ]
14501450 assert tols ['atol' ] == linear_solve_defaults ['ksp_atol' ]
1451- assert tols ['dtol ' ] == linear_solve_defaults ['ksp_divtol' ]
1452- assert tols ['maxits ' ] == linear_solve_defaults ['ksp_max_it' ]
1451+ assert tols ['divtol ' ] == linear_solve_defaults ['ksp_divtol' ]
1452+ assert tols ['max_it ' ] == linear_solve_defaults ['ksp_max_it' ]
14531453
14541454 @skipif ('petsc' )
14551455 @pytest .mark .parametrize ('log_level' , ['PERF' , 'DEBUG' ])
@@ -1655,8 +1655,8 @@ def test_tolerances(self, log_level):
16551655 # appear as expected in the `PetscSummary`.
16561656 assert tolerances ['rtol' ] == params ['ksp_rtol' ]
16571657 assert tolerances ['atol' ] == params ['ksp_atol' ]
1658- assert tolerances ['dtol ' ] == params ['ksp_divtol' ]
1659- assert tolerances ['maxits ' ] == params ['ksp_max_it' ]
1658+ assert tolerances ['divtol ' ] == params ['ksp_divtol' ]
1659+ assert tolerances ['max_it ' ] == params ['ksp_max_it' ]
16601660
16611661 @skipif ('petsc' )
16621662 def test_clearing_options (self ):
@@ -1805,3 +1805,117 @@ def test_solveexpr(self):
18051805 options_prefix = 'poisson3'
18061806 )
18071807 assert hash (petsc3 .rhs ) != hash (petsc4 .rhs )
1808+
1809+
1810+ class TestGetInfo :
1811+ """
1812+ Test the `get_info` optional argument to `PETScSolve`.
1813+
1814+ This argument can be used independently of the `log_level` to retrieve
1815+ specific information about the solve, such as the number of KSP
1816+ iterations to converge.
1817+ """
1818+ @skipif ('petsc' )
1819+ def test_get_info (self ):
1820+
1821+ grid = Grid (shape = (11 , 11 ), dtype = np .float64 )
1822+ functions = [Function (name = n , grid = grid , space_order = 2 )
1823+ for n in ['e' , 'f' ]]
1824+ e , f = functions
1825+ eq = Eq (e .laplace , f )
1826+
1827+ get_info = ['kspgetiterationnumber' , 'snesgetiterationnumber' ]
1828+
1829+ petsc = PETScSolve (
1830+ eq , target = e , options_prefix = 'pde1' , get_info = get_info
1831+ )
1832+
1833+ with switchconfig (language = 'petsc' ):
1834+ op = Operator (petsc )
1835+ summary = op .apply ()
1836+
1837+ petsc_summary = summary .petsc
1838+ entry = petsc_summary .get_entry ('section0' , 'pde1' )
1839+
1840+ # Verify that the entry contains only the requested info
1841+ # (since logging is not set)
1842+ assert len (entry ) == 2
1843+ assert hasattr (entry , "KSPGetIterationNumber" )
1844+ assert hasattr (entry , "SNESGetIterationNumber" )
1845+
1846+ @skipif ('petsc' )
1847+ @pytest .mark .parametrize ('log_level' , ['PERF' , 'DEBUG' ])
1848+ def test_get_info_with_logging (self , log_level ):
1849+ """
1850+ Test that `get_info` works correctly when logging is enabled.
1851+ """
1852+ grid = Grid (shape = (11 , 11 ), dtype = np .float64 )
1853+ functions = [Function (name = n , grid = grid , space_order = 2 )
1854+ for n in ['e' , 'f' ]]
1855+ e , f = functions
1856+ eq = Eq (e .laplace , f )
1857+
1858+ get_info = ['kspgetiterationnumber' ]
1859+
1860+ petsc = PETScSolve (
1861+ eq , target = e , options_prefix = 'pde1' , get_info = get_info
1862+ )
1863+
1864+ with switchconfig (language = 'petsc' , log_level = log_level ):
1865+ op = Operator (petsc )
1866+ summary = op .apply ()
1867+
1868+ petsc_summary = summary .petsc
1869+ entry = petsc_summary .get_entry ('section0' , 'pde1' )
1870+
1871+ # With logging enabled, the entry should include both the
1872+ # requested KSP iteration number and additional PETSc info
1873+ # (e.g., SNES iteration count logged at PERF/DEBUG).
1874+ assert len (entry ) > 1
1875+ assert hasattr (entry , "KSPGetIterationNumber" )
1876+ assert hasattr (entry , "SNESGetIterationNumber" )
1877+
1878+ @skipif ('petsc' )
1879+ def test_different_solvers (self ):
1880+ """
1881+ Test that `get_info` works correctly when multiple solvers are used
1882+ within the same Operator.
1883+ """
1884+ grid = Grid (shape = (11 , 11 ), dtype = np .float64 )
1885+ functions = [Function (name = n , grid = grid , space_order = 2 )
1886+ for n in ['e' , 'f' , 'g' , 'h' ]]
1887+ e , f , g , h = functions
1888+
1889+ eq1 = Eq (e .laplace , f )
1890+ eq2 = Eq (g .laplace , h )
1891+
1892+ # Create two PETScSolve instances with different get_info arguments
1893+
1894+ get_info_1 = ['kspgetiterationnumber' ]
1895+ get_info_2 = ['snesgetiterationnumber' ]
1896+
1897+ solver1 = PETScSolve (
1898+ eq1 , target = e , options_prefix = 'pde1' , get_info = get_info_1
1899+ )
1900+ solver2 = PETScSolve (
1901+ eq2 , target = g , options_prefix = 'pde2' , get_info = get_info_2
1902+ )
1903+
1904+ with switchconfig (language = 'petsc' ):
1905+ op = Operator ([solver1 , solver2 ])
1906+ summary = op .apply ()
1907+
1908+ petsc_summary = summary .petsc
1909+
1910+ assert len (petsc_summary ) == 2
1911+ assert len (petsc_summary .KSPGetIterationNumber ) == 1
1912+ assert len (petsc_summary .SNESGetIterationNumber ) == 1
1913+
1914+ entry1 = petsc_summary .get_entry ('section0' , 'pde1' )
1915+ entry2 = petsc_summary .get_entry ('section1' , 'pde2' )
1916+
1917+ assert hasattr (entry1 , "KSPGetIterationNumber" )
1918+ assert not hasattr (entry1 , "SNESGetIterationNumber" )
1919+
1920+ assert not hasattr (entry2 , "KSPGetIterationNumber" )
1921+ assert hasattr (entry2 , "SNESGetIterationNumber" )
0 commit comments