Skip to content
Open
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
375cbde
FEAT: Provide set of classes for organized time stepping
jwboth May 25, 2026
11d8cb2
MAINT: Simplify model runner and integrate external time stepping
jwboth May 25, 2026
955e606
MAINT: Remove flowchart character from TimeManager, now performed by …
jwboth May 25, 2026
8fc1e76
MAINT: Add utility method for updated progressbar handling
jwboth May 25, 2026
605c8c7
FEAT: Provide set of classes for organized time stepping
jwboth May 25, 2026
5cc1c6b
MAINT: Simplify model runner and integrate external time stepping
jwboth May 25, 2026
30f8772
MAINT: Remove flowchart character from TimeManager, now performed by …
jwboth May 25, 2026
f01086e
MAINT: Add utility method for updated progressbar handling
jwboth May 25, 2026
1d4c8ed
DOC: Clean up of comments
jwboth May 31, 2026
c1110e1
Merge branch 'trial-time' of https://github.com/pmgbergen/porepy into…
jwboth May 31, 2026
8bddaa7
Merge branch 'develop' into trial-time
Yuriyzabegaev Jun 12, 2026
94d3383
MAINT: Renamed convergence statuses
Yuriyzabegaev Jun 22, 2026
95b2908
MAINT: Splitted simulation status into time stepper status and model …
Yuriyzabegaev Jun 23, 2026
4ff90e9
WIP: Making some tests pass
Yuriyzabegaev Jun 23, 2026
4f32c65
TEST: Made some more tests work
Yuriyzabegaev Jun 23, 2026
a323124
TEST: Some more tests
Yuriyzabegaev Jun 23, 2026
5f157cd
MAINT: Removed commented out code
Yuriyzabegaev Jun 24, 2026
b028e39
MAINT: Added new methods to the protocol
Yuriyzabegaev Jun 24, 2026
2286b5f
TEST: Some more tests pass
Yuriyzabegaev Jun 24, 2026
009a7d9
TEST: Additional tests for time stepper
Yuriyzabegaev Jun 24, 2026
0ef5e50
TEST: Added test for TimeStepper
Yuriyzabegaev Jun 24, 2026
0f7ee3f
TEST: Moved test_model_time_step_control
Yuriyzabegaev Jun 24, 2026
00b07ae
DOC: Updated some docstrings
Yuriyzabegaev Jun 25, 2026
3359bf1
Maint: Nonlinear solver status
Yuriyzabegaev Jun 25, 2026
a82b71c
MAINT: Ruff, isort
Yuriyzabegaev Jun 25, 2026
7eb9a3d
Merge branch 'develop' into yz-time-trial-2 (tests fail)
Yuriyzabegaev Jun 25, 2026
a3052b2
MAINT: Failed simulation raises RuntimeError
Yuriyzabegaev Jun 25, 2026
7ee86ea
MAINT: Resolved circular import
Yuriyzabegaev Jun 25, 2026
afec095
TEST: Fixed a test
Yuriyzabegaev Jun 25, 2026
9dcfab0
TEST: Updated test_solver_statistics with new statuses
Yuriyzabegaev Jun 25, 2026
885c7be
MAINT: Removed old comment
Yuriyzabegaev Jun 25, 2026
6018364
TEST: Updated some tests
Yuriyzabegaev Jun 25, 2026
6195f83
TEST: Fixed some tests
Yuriyzabegaev Jun 25, 2026
251ee5f
TEST: Some more tests pass
Yuriyzabegaev Jun 25, 2026
cb0514c
TEST: Interplay between statistics and TimeStepper
Yuriyzabegaev Jun 26, 2026
95aa50a
MAINT: Small improvements
Yuriyzabegaev Jun 26, 2026
a9a02db
MAINT: Isort tests
Yuriyzabegaev Jun 26, 2026
ce38af6
TEST: Last moment test fix
Yuriyzabegaev Jun 26, 2026
ae3fd0f
Merge branch 'develop' into trial-time
Yuriyzabegaev Jun 26, 2026
b41832d
MAINT: python3.13 fix
Yuriyzabegaev Jun 26, 2026
cadc1dc
MAINT: Attempt to improve progressbar
Yuriyzabegaev Jun 26, 2026
d47116a
MAINT: Attempt to improve progressbar 2
Yuriyzabegaev Jun 26, 2026
bd1b96c
MAINT: Updated combined divergence criterion
Yuriyzabegaev Jun 29, 2026
7c86bad
MAINT: reverted old behavior that divergence criteria return either C…
Yuriyzabegaev Jun 29, 2026
124a411
DOC: Added clarification on how to subclass state enums
Yuriyzabegaev Jun 29, 2026
3018824
MAINT: replaced match with isinstance
Yuriyzabegaev Jun 29, 2026
0a38c74
MAINT: Moved common parts of NewtonSolver and LinearSolver into a sep…
Yuriyzabegaev Jun 29, 2026
8563978
MAINT: Moved TimeStepManager to time module
Yuriyzabegaev Jun 29, 2026
14b703c
DOC: time stepper docstring
Yuriyzabegaev Jun 29, 2026
0e2f3f4
MAINT: Renamed is_diverged to is_failed
Yuriyzabegaev Jun 29, 2026
0d154c8
MAINT: isort
Yuriyzabegaev Jun 29, 2026
4a90a37
MAINT: renamed time folder into time_stepper
Yuriyzabegaev Jun 30, 2026
ae40ba0
MAINT: ruff
Yuriyzabegaev Jun 30, 2026
a0b44a2
Merge branch 'develop' into trial-time
keileg Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 105 additions & 205 deletions src/porepy/models/model_runner.py

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions src/porepy/models/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,9 @@ def _is_reference_component_eliminated(self) -> bool:
"""Returns True if ``params['eliminate_reference_component'] == True`.
Defaults to True."""

def before_time_step(self) -> None:
Comment thread
keileg marked this conversation as resolved.
"""Called at the start of each time step by model runners."""

def before_nonlinear_loop(self) -> None:
"""Method to be called before the non-linear loop.

Expand All @@ -689,6 +692,12 @@ def after_nonlinear_convergence(self) -> None:

"""

def after_time_step_convergence(self) -> None:
"""Called after a new time step solution has been achieved."""

def after_time_step_failure(self) -> None:
"""Called after a time step has failed to converge."""

def set_nonlinear_discretizations(self) -> None:
"""Set the list of nonlinear discretizations.

Expand Down
119 changes: 69 additions & 50 deletions src/porepy/numerics/linear_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@

from __future__ import annotations

import logging
from typing import TYPE_CHECKING
from warnings import warn

from porepy.models.solution_strategy import SolutionStrategy
from porepy.numerics.nonlinear.convergence_check import (
ConvergenceCriteria,
ConvergenceInfoCollection,
ConvergenceStatus,
ConvergenceStatusCollection,
DivergenceCriteria,
IncrementBasedNanCriterion,
ResidualBasedNanCriterion,
SolverStatus,
)
from porepy.viz.solver_statistics import TimeStatistics
from porepy.numerics.nonlinear.nonlinear_solver_status import (
NonlinearSolverStatus,
NonlinearSolverStatusConverged,
NonlinearSolverStatusFailed,
)

if TYPE_CHECKING:
import numpy as np

logger = logging.getLogger(__name__)


class LinearSolver:
"""Base solver class for PorePy models, assuming the model is linear and performing
Expand Down Expand Up @@ -60,7 +66,7 @@ def __init__(self, params: dict | None = None) -> None:
)
"""Divergence criterion used in the convergence check."""

def solve(self, model: SolutionStrategy) -> SolverStatus:
def solve(self, model: SolutionStrategy) -> NonlinearSolverStatus:
"""Solve a linear problem defined by the current state of the model.

The linear solver performs only one iteration and checks whether it converged.
Expand All @@ -87,11 +93,58 @@ def solve(self, model: SolutionStrategy) -> SolverStatus:

# Conclude on the solver status.
solver_status = self.summarize_solver_status(
model, convergence_status, divergence_status
convergence_status, divergence_status
)

# Logging statistics.
self.update_solver_statistics(model=model, solver_status=solver_status)

if solver_status.is_converged():
model.after_nonlinear_convergence()
else:
model.after_nonlinear_failure()

return solver_status

def summarize_solver_status(
self,
convergence_status: ConvergenceStatusCollection,
divergence_status: ConvergenceStatusCollection,
) -> NonlinearSolverStatus:
"""Consider a collection of convergence and divergence statuses from multiple
criteria and make an overall verdict on whether we accept the sollution or not.

Parameters:
convergence_status: Multiple convergence statuses from different criteria.
divergence_status: Multiple divergence statuses from variaous criteria.

Returns:
NonlinearSolverStatus: Either Converged or Failed.

"""
# YZ: This is a duplicated method of NewtonSolver, but I find it acceptible for
Comment thread
Yuriyzabegaev marked this conversation as resolved.
Outdated
# now since we are planning to unify these two classes in the nearest future.
if convergence_status.is_converged():
return NonlinearSolverStatusConverged(
convergence_statuses=convergence_status,
divergence_statuses=divergence_status,
)
elif divergence_status.is_diverged():
logger.warning("Failed to solve the nonlinear problem.")
return NonlinearSolverStatusFailed(
convergence_statuses=convergence_status,
divergence_statuses=divergence_status,
)
else:
logger.error(
"Nonlinear solver did not fail, but the convergence criterion did not "
"accept the solution. Treating it as a failure."
)
return NonlinearSolverStatusFailed(
convergence_statuses=convergence_status,
divergence_statuses=divergence_status,
)

def before_linear_solve(self, model: SolutionStrategy) -> None:
"""Prepare the linear solve.

Expand Down Expand Up @@ -135,42 +188,6 @@ def linear_solve(

return convergence_status, divergence_status

def summarize_solver_status(
self,
model: SolutionStrategy,
convergence_status: ConvergenceStatusCollection,
divergence_status: ConvergenceStatusCollection,
) -> SolverStatus:
"""Conclude on the overall solver status.

NOTE: Convergence status takes precedence over divergence status.

Parameters:
model: The model instance specifying the problem to be solved.
convergence_status: Convergence statuses.
divergence_status: Divergence statuses.

Returns:
SolverStatus: The overall status of the nonlinear solver.

"""
if convergence_status.is_converged():
solver_status = SolverStatus.SUCCESSFUL
self.update_solver_statistics(model, solver_status)
model.after_nonlinear_convergence()
elif divergence_status.is_diverged():
solver_status = SolverStatus.FAILED
self.update_solver_statistics(model, solver_status)
model.after_nonlinear_failure()
warn("Failed to solve the (non)linear problem.", UserWarning)
else:
raise ValueError(
"Invalid convergence status: "
f"{convergence_status.union(divergence_status)}"
)

return solver_status

def after_linear_iteration(
self, model: SolutionStrategy, nonlinear_increment: np.ndarray
) -> tuple[ConvergenceStatusCollection, ConvergenceStatusCollection]:
Expand Down Expand Up @@ -223,25 +240,35 @@ def check_convergence(
"""
# Fetch the residual.
residual = model.equation_system.assemble(evaluate_jacobian=False)
iterate = model.equation_system.get_variable_values(iterate_index=0)

# Check convergence status based on current iteration.
convergence_status, convergence_info = self.convergence_criteria.check(
increment=nonlinear_increment,
residual=residual,
# Reference values don't make sense for a linear solver, but passed to
Comment thread
keileg marked this conversation as resolved.
# conform the interface defined by the NewtonSolver.
reference_increment=iterate,
reference_residual=residual,
)

# Check divergence status based on current iteration.
divergence_status = self.divergence_criteria.check(
increment=nonlinear_increment,
residual=residual,
# Reference values and num_iterations don't make sense for a linear solver,
# but passed to conform the interface defined by the NewtonSolver.
reference_increment=iterate,
reference_residual=residual,
num_iterations=1,
)

return convergence_status, divergence_status, convergence_info

def update_solver_statistics(
self,
model: SolutionStrategy,
solver_status: SolverStatus,
solver_status: NonlinearSolverStatus,
) -> None:
"""Update the solver statistics in the model.

Expand All @@ -254,11 +281,3 @@ def update_solver_statistics(
# Basic discretization-related information and overall simulation status.
model.nonlinear_solver_statistics.log_solver_status(solver_status)
model.nonlinear_solver_statistics.log_mesh_information(model.mdg.subdomains())
if model._is_time_dependent():
assert isinstance(model.nonlinear_solver_statistics, TimeStatistics)
model.nonlinear_solver_statistics.log_time_information(
model.time_manager.time_index,
model.time_manager.time,
model.time_manager.dt,
model.time_manager.final_time_reached(),
)
Loading
Loading