# Solver Configuration Entropyk provides multiple solver strategies for thermodynamic system simulation. ## Overview | Solver | Convergence | Use Case | |--------|-------------|----------| | **Newton-Raphson** | Quadratic | Well-conditioned systems with good initial guess | | **Picard** | Linear | Strongly nonlinear systems, more robust | | **Fallback** | Adaptive | Automatic Newton→Picard fallback on divergence | ## Newton-Raphson Solver The Newton-Raphson method solves F(x) = 0 by iteratively computing: ``` x_{n+1} = x_n - J^{-1} * F(x_n) ``` Where J is the Jacobian matrix of partial derivatives. ### Configuration ```rust use entropyk_solver::{NewtonConfig, NewtonSolver, ConvergenceCriteria}; let config = NewtonConfig { max_iterations: 100, tolerance: 1e-6, relaxation: 1.0, // Damping factor (0 < α ≤ 1) ..Default::default() }; let solver = NewtonSolver::new(config); ``` ### With Convergence Criteria ```rust let criteria = ConvergenceCriteria { pressure_tolerance: 100.0, // Pa enthalpy_tolerance: 1000.0, // J/kg residual_tolerance: 1e-6, ..Default::default() }; let solver = NewtonSolver::new(NewtonConfig::default()) .with_convergence_criteria(criteria); ``` ### With Initial State ```rust // Provide initial guess for state vector let initial_state = vec![ 1e6, // P_edge0 (Pa) 400e3, // h_edge0 (J/kg) 0.9e6, // P_edge1 (Pa) 250e3, // h_edge1 (J/kg) ]; let solver = NewtonSolver::new(NewtonConfig::default()) .with_initial_state(initial_state); ``` ### Jacobian Freezing For performance, the Jacobian can be reused across iterations: ```rust use entropyk_solver::JacobianFreezingConfig; let freeze_config = JacobianFreezingConfig { max_frozen_iterations: 5, // Reuse J for up to 5 iterations recompute_on_divergence: true, }; let solver = NewtonSolver::new(NewtonConfig { jacobian_freezing: Some(freeze_config), ..Default::default() }); ``` ## Picard Solver The Picard (fixed-point) iteration is more robust for strongly nonlinear systems: ``` x_{n+1} = G(x_n) ``` ### Configuration ```rust use entropyk_solver::{PicardConfig, PicardSolver}; let config = PicardConfig { max_iterations: 200, tolerance: 1e-5, relaxation: 0.8, // Under-relaxation for stability ..Default::default() }; let solver = PicardSolver::new(config); ``` ### When to Use Picard - **Phase change problems**: Evaporation/condensation with sharp property changes - **Poor initial guess**: When Newton diverges - **Near singularities**: Close to critical points ## Fallback Solver The Fallback solver automatically switches from Newton to Picard on divergence: ```rust use entropyk_solver::{FallbackConfig, FallbackSolver}; let config = FallbackConfig { newton_max_iterations: 50, picard_max_iterations: 100, switch_on_newton_divergence: true, ..Default::default() }; let solver = FallbackSolver::new(config); ``` ### Fallback Behavior 1. Start with Newton-Raphson 2. If Newton diverges (residual increases), switch to Picard 3. If Picard converges, optionally try Newton again for faster convergence ## Convergence Criteria ### Basic Tolerance ```rust let criteria = ConvergenceCriteria { pressure_tolerance: 100.0, // |ΔP| < 100 Pa enthalpy_tolerance: 1000.0, // |Δh| < 1000 J/kg residual_tolerance: 1e-6, // ||F(x)|| < 1e-6 max_iterations: 100, }; ``` ### Per-Circuit Convergence For multi-circuit systems, convergence is tracked per circuit: ```rust // After solving, check the convergence report if let Some(report) = converged_state.convergence_report() { for circuit in &report.circuits { println!( "Circuit {}: converged={}, max_ΔP={:.1} Pa, max_Δh={:.1} J/kg", circuit.circuit_id, circuit.converged, circuit.max_pressure_change, circuit.max_enthalpy_change ); } } ``` ## Smart Initialization Good initial guess is critical for convergence. Use `SmartInitializer`: ```rust use entropyk_solver::{SmartInitializer, InitializerConfig}; let config = InitializerConfig { fluid: "R134a".to_string(), source_temperature_celsius: 40.0, // Condensing temperature sink_temperature_celsius: 5.0, // Evaporating temperature superheat_kelvin: 5.0, subcool_kelvin: 3.0, }; let initializer = SmartInitializer::new(config); // Estimate saturation pressures let (p_high, p_low) = initializer.estimate_pressures( 40.0, // Source temperature (°C) 5.0, // Sink temperature (°C) ); // Populate state vector let state = initializer.populate_state(&system, 40.0, 5.0)?; ``` ## Running the Solver ### Basic Solve ```rust use entropyk_solver::{System, NewtonSolver, NewtonConfig}; fn main() -> Result<(), Box> { let mut system = build_system(); system.finalize()?; let solver = NewtonSolver::new(NewtonConfig::default()); let result = solver.solve(&system)?; println!("Converged in {} iterations", result.iterations); println!("Final state: {:?}", result.state); Ok(()) } ``` ### With Timeout ```rust use entropyk_solver::TimeoutConfig; let config = NewtonConfig { timeout: Some(TimeoutConfig { max_time_seconds: 10.0, return_best_on_timeout: true, }), ..Default::default() }; ``` ## Error Handling ```rust use entropyk_solver::SolverError; match solver.solve(&system) { Ok(result) => { println!("Converged: {:?}", result.status); } Err(SolverError::MaxIterationsExceeded { iterations, residual }) => { eprintln!("Failed to converge after {} iterations", iterations); eprintln!("Final residual: {}", residual); } Err(SolverError::JacobianSingular { condition_number }) => { eprintln!("Jacobian is singular (condition number: {})", condition_number); eprintln!("Check system topology and initial guess"); } Err(e) => { eprintln!("Solver error: {:?}", e); } } ``` ## Solver Strategy Selection ```rust use entropyk_solver::SolverStrategy; let strategy = SolverStrategy::Fallback; // Recommended for production match strategy { SolverStrategy::Newton => { let solver = NewtonSolver::new(NewtonConfig::default()); } SolverStrategy::Picard => { let solver = PicardSolver::new(PicardConfig::default()); } SolverStrategy::Fallback => { let solver = FallbackSolver::new(FallbackConfig::default()); } } ``` ## Best Practices 1. **Always use SmartInitializer** - Good initial guess is critical 2. **Use Fallback solver** - More robust for production use 3. **Set appropriate tolerances** - Balance accuracy vs. convergence time 4. **Monitor convergence** - Log iteration progress for debugging 5. **Handle errors gracefully** - Provide meaningful feedback on failure ## Python Bindings ```python import entropyk # Note: Python bindings use placeholder adapters # The solver won't converge because there are no real physics equations system = entropyk.System() # ... build system ... solver = entropyk.NewtonSolver( max_iterations=100, tolerance=1e-6 ) result = solver.solve(system) print(f"Status: {result.status}") print(f"Iterations: {result.iterations}") ``` ## Next Steps - [Components Reference](./03-components.md) - Detailed component documentation - [Building Systems](./04-building-systems.md) - Create system topology