#==================
# [Link] (with adaptive alpha strategy)
#==================
import numpy as np
from [Link] 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):
[Link] = assembly
self.n_increments = n_increments
[Link] = 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 = [Link](assembly.n_dof)
self.residual_norms = []
self.convergence_data = []
def solve(self):
"""Solve with adaptive alpha strategy"""
# Initialize displacement vector
[Link].u = [Link]([Link].n_dof)
# Apply gravity (constant)
[Link].F_ext = [Link]([Link].n_dof)
[Link].apply_gravity()
# Initialize load factor
load_factor = 0.0
load_increment = 1.0 / self.n_increments
# Time the solution
start_time = [Link]()
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
[Link].apply_hydrostatic_pressure(load_factor * 95)
[Link].apply_uplift_pressure(
upstream_level=load_factor * 1000*9.81*95,
downstream_level=0
)
# Save state at start of increment
current_state = [Link].save_state()
alpha_reductions = 0
converged = False
while alpha_reductions <= self.max_alpha_reductions and not converged:
# Restore to start of increment
[Link].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 = [Link]() - 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
[Link].assemble_stiffness(tangent=False)
for iter in range(self.max_iter):
# Compute internal forces
F_int = [Link].compute_internal_forces()
# Compute residual
R = [Link].F_ext - F_int
norm_R = [Link](R)
self.residual_norms.append(norm_R)
# Apply boundary conditions
K_mod, R_mod = [Link].apply_boundary_conditions([Link].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
[Link].u += alpha * du
self.u = [Link].u
# Check convergence
norm_du = [Link](alpha * du)
print(f"Iter {iter+1}: |R| = {norm_R:.4e}, |Δu| = {norm_du:.4e}, α =
{alpha:.4f}")
if norm_R < [Link] and norm_du < [Link]:
return True
# Update tangent stiffness
[Link].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 = [Link]()
norm_R0 = [Link](R0)
min_alpha = 0.01 # Minimum allowed step size
for i in range(self.ls_max):
# Trial displacement
[Link].u = u_prev + alpha * du
# Compute new residual
F_int = [Link].compute_internal_forces()
R_new = [Link].F_ext - F_int
norm_R_new = [Link](R_new)
# Armijo condition: sufficient decrease
if norm_R_new <= norm_R0 + self.ls_c1 * alpha * [Link](R0, du):
return alpha
# Reduce step size using current base reduction factor
alpha *= base_alpha
if alpha < min_alpha:
return min_alpha
return alpha