0% found this document useful (0 votes)
28 views3 pages

Nonlinear Solver

The document describes a NonlinearSolver class that implements an incremental-iterative solver with an adaptive alpha strategy for solving nonlinear problems. It includes methods for applying external forces, solving increments, and performing an Armijo line search to ensure convergence. The solver tracks convergence data and manages load increments while adapting the line search parameters based on the convergence behavior.

Uploaded by

Manoj Baral
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
28 views3 pages

Nonlinear Solver

The document describes a NonlinearSolver class that implements an incremental-iterative solver with an adaptive alpha strategy for solving nonlinear problems. It includes methods for applying external forces, solving increments, and performing an Armijo line search to ensure convergence. The solver tracks convergence data and manages load increments while adapting the line search parameters based on the convergence behavior.

Uploaded by

Manoj Baral
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 3

#==================

# solver.py (with adaptive alpha strategy)


#==================
import numpy as np
from scipy.sparse.linalg import spsolve
import time

class NonlinearSolver:
"""Incremental-iterative solver with adaptive alpha strategy"""
def __init__(self, assembly, n_increments=10, tol=1e-6, max_iter=20,
ls_max=10, ls_alpha=0.5, ls_c1=1e-4,
alpha_reduction=0.5, max_alpha_reductions=3):
self.assembly = assembly
self.n_increments = n_increments
self.tol = tol
self.max_iter = max_iter
self.ls_max = ls_max
self.ls_alpha = ls_alpha
self.ls_c1 = ls_c1
self.alpha_reduction = alpha_reduction
self.max_alpha_reductions = max_alpha_reductions
self.u = np.zeros(assembly.n_dof)
self.residual_norms = []
self.convergence_data = []

def solve(self):
"""Solve with adaptive alpha strategy"""
# Initialize displacement vector
self.assembly.u = np.zeros(self.assembly.n_dof)

# Apply gravity (constant)


self.assembly.F_ext = np.zeros(self.assembly.n_dof)
self.assembly.apply_gravity()

# Initialize load factor


load_factor = 0.0
load_increment = 1.0 / self.n_increments

# Time the solution


start_time = time.time()

for inc in range(1, self.n_increments + 1):


# Update load factor
load_factor += load_increment
print(f"\n--- Load Increment {inc}/{self.n_increments}
(λ={load_factor:.2f}) ---")

# Apply fluid pressures


self.assembly.apply_hydrostatic_pressure(load_factor * 95)
self.assembly.apply_uplift_pressure(
upstream_level=load_factor * 1000*9.81*95,
downstream_level=0
)

# Save state at start of increment


current_state = self.assembly.save_state()
alpha_reductions = 0
converged = False
while alpha_reductions <= self.max_alpha_reductions and not converged:
# Restore to start of increment
self.assembly.restore_state(current_state)

# Adjust line search parameters


current_ls_alpha = self.ls_alpha * (self.alpha_reduction **
alpha_reductions)

# Attempt to solve increment with current alpha


converged = self.solve_increment(current_ls_alpha)

if not converged:
alpha_reductions += 1
print(f"Alpha reduction #{alpha_reductions} (new
α={current_ls_alpha:.4f})")

if converged:
print(f"Converged with α={current_ls_alpha:.4f}")
self.convergence_data.append((load_factor, alpha_reductions))
else:
print(f"Warning: Failed to converge in increment {inc}")
break

total_time = time.time() - start_time


print(f"\nSolution completed in {total_time:.2f} seconds")
print(f"Total iterations: {len(self.residual_norms)}")
print(f"Final load factor: {load_factor:.4f}")

return self.u

def solve_increment(self, current_ls_alpha):


"""Solve current increment with specified line search alpha"""
# Initial stiffness assembly
self.assembly.assemble_stiffness(tangent=False)

for iter in range(self.max_iter):


# Compute internal forces
F_int = self.assembly.compute_internal_forces()

# Compute residual
R = self.assembly.F_ext - F_int
norm_R = np.linalg.norm(R)
self.residual_norms.append(norm_R)

# Apply boundary conditions


K_mod, R_mod = self.assembly.apply_boundary_conditions(self.assembly.K,
R)

# Solve for displacement increment


try:
du = spsolve(K_mod, R_mod)
except Exception as e:
print(f"Linear solver error: {str(e)}")
return False

# Perform line search with current alpha


alpha = self.armijo_line_search(du, R, current_ls_alpha)

# Update displacements
self.assembly.u += alpha * du
self.u = self.assembly.u

# Check convergence
norm_du = np.linalg.norm(alpha * du)
print(f"Iter {iter+1}: |R| = {norm_R:.4e}, |Δu| = {norm_du:.4e}, α =
{alpha:.4f}")

if norm_R < self.tol and norm_du < self.tol:


return True

# Update tangent stiffness


self.assembly.assemble_stiffness(tangent=True)

return False

def armijo_line_search(self, du, R0, base_alpha):


"""Armijo line search with specified base alpha"""
alpha = 1.0 # Start with full step
u_prev = self.assembly.u.copy()
norm_R0 = np.linalg.norm(R0)
min_alpha = 0.01 # Minimum allowed step size

for i in range(self.ls_max):
# Trial displacement
self.assembly.u = u_prev + alpha * du

# Compute new residual


F_int = self.assembly.compute_internal_forces()
R_new = self.assembly.F_ext - F_int
norm_R_new = np.linalg.norm(R_new)

# Armijo condition: sufficient decrease


if norm_R_new <= norm_R0 + self.ls_c1 * alpha * np.dot(R0, du):
return alpha

# Reduce step size using current base reduction factor


alpha *= base_alpha

if alpha < min_alpha:


return min_alpha

return alpha

You might also like