# Physics Loss Functions Guide **Selecting and configuring loss functions for AtomizerField training** --- ## Overview AtomizerField uses physics-informed loss functions to train neural networks that respect engineering principles. This guide explains each loss function and when to use them. --- ## Available Loss Functions | Loss Function | Purpose | Best For | |--------------|---------|----------| | **MSE Loss** | Standard L2 error | General training, balanced outputs | | **Relative Loss** | Percentage error | Multi-scale outputs (MPa + mm) | | **Physics-Informed Loss** | Enforce physics | Better generalization, extrapolation | | **Max Error Loss** | Penalize outliers | Safety-critical applications | | **Combined Loss** | Weighted combination | Production models | --- ## 1. MSE Loss (Mean Squared Error) ### Description Standard L2 loss that treats all predictions equally. ```python loss = mean((predicted - target)²) ``` ### Implementation ```python def mse_loss(predicted, target): """Simple MSE loss""" return torch.mean((predicted - target) ** 2) ``` ### When to Use - Starting point for new models - When all outputs have similar magnitudes - When you don't have physics constraints ### Pros & Cons | Pros | Cons | |------|------| | Simple and stable | Ignores physics | | Fast computation | Scale-sensitive | | Well-understood | Large errors dominate | --- ## 2. Relative Loss ### Description Computes percentage error instead of absolute error. Critical for multi-scale outputs. ```python loss = mean(|predicted - target| / |target|) ``` ### Implementation ```python def relative_loss(predicted, target, epsilon=1e-8): """Relative (percentage) loss""" relative_error = torch.abs(predicted - target) / (torch.abs(target) + epsilon) return torch.mean(relative_error) ``` ### When to Use - Outputs have different scales (stress in MPa, displacement in mm) - Percentage accuracy matters more than absolute accuracy - Training data has wide range of values ### Pros & Cons | Pros | Cons | |------|------| | Scale-independent | Unstable near zero | | Intuitive (% error) | Requires epsilon | | Equal weight to all magnitudes | May overfit small values | ### Example ```python # Without relative loss stress_error = |100 MPa - 105 MPa| = 5 MPa displacement_error = |0.01 mm - 0.02 mm| = 0.01 mm # MSE dominated by stress, displacement ignored # With relative loss stress_error = |5| / |100| = 5% displacement_error = |0.01| / |0.01| = 100% # Both contribute proportionally ``` --- ## 3. Physics-Informed Loss ### Description Adds physics constraints as regularization terms. The network learns to satisfy physical laws. ```python loss = mse_loss + λ₁·equilibrium + λ₂·constitutive + λ₃·boundary ``` ### Implementation ```python def physics_informed_loss(predicted, target, data, config): """ Physics-informed loss with multiple constraint terms. Components: 1. Data loss (MSE) 2. Equilibrium loss (F = ma) 3. Constitutive loss (σ = Eε) 4. Boundary condition loss (u = 0 at supports) """ # Data loss data_loss = mse_loss(predicted, target) # Equilibrium loss: sum of forces at each node = 0 equilibrium_loss = compute_equilibrium_residual( predicted['displacement'], data.edge_index, data.stiffness ) # Constitutive loss: stress-strain relationship predicted_stress = compute_stress_from_displacement( predicted['displacement'], data.material, data.strain_operator ) constitutive_loss = mse_loss(predicted['stress'], predicted_stress) # Boundary condition loss: fixed nodes have zero displacement bc_mask = data.boundary_conditions > 0 bc_loss = torch.mean(predicted['displacement'][bc_mask] ** 2) # Combine with weights total_loss = ( data_loss + config.lambda_equilibrium * equilibrium_loss + config.lambda_constitutive * constitutive_loss + config.lambda_bc * bc_loss ) return total_loss ``` ### Physics Constraints #### Equilibrium (Force Balance) At each node, the sum of forces must be zero: ``` ∑F = 0 at every node ``` ```python def equilibrium_residual(displacement, stiffness_matrix): """ Check if Ku = F (stiffness × displacement = force) Residual should be zero for valid solutions. """ internal_forces = stiffness_matrix @ displacement external_forces = get_external_forces() residual = internal_forces - external_forces return torch.mean(residual ** 2) ``` #### Constitutive (Stress-Strain) Stress must follow material law: ``` σ = Eε (Hooke's law) ``` ```python def constitutive_residual(displacement, stress, material): """ Check if stress follows constitutive law. """ strain = compute_strain(displacement) predicted_stress = material.E * strain residual = stress - predicted_stress return torch.mean(residual ** 2) ``` #### Boundary Conditions Fixed nodes must have zero displacement: ```python def boundary_residual(displacement, bc_mask): """ Fixed nodes should have zero displacement. """ return torch.mean(displacement[bc_mask] ** 2) ``` ### When to Use - When you need good generalization - When extrapolating beyond training data - When physical correctness is important - When training data is limited ### Pros & Cons | Pros | Cons | |------|------| | Physics consistency | More computation | | Better extrapolation | Requires physics info | | Works with less data | Weight tuning needed | ### Weight Selection | Constraint | Typical λ | Notes | |------------|-----------|-------| | Equilibrium | 0.1 - 0.5 | Most important | | Constitutive | 0.05 - 0.2 | Material law | | Boundary | 0.5 - 1.0 | Hard constraint | --- ## 4. Max Error Loss ### Description Penalizes the worst predictions. Critical for safety-critical applications. ```python loss = max(|predicted - target|) ``` ### Implementation ```python def max_error_loss(predicted, target, percentile=99): """ Penalize worst predictions. Uses percentile to avoid single outlier domination. """ errors = torch.abs(predicted - target) # Use percentile instead of max for stability max_error = torch.quantile(errors, percentile / 100.0) return max_error ``` ### When to Use - Safety-critical applications - When outliers are unacceptable - Quality assurance requirements - Certification contexts ### Pros & Cons | Pros | Cons | |------|------| | Controls worst case | Unstable gradients | | Safety-focused | May slow convergence | | Clear metric | Sensitive to outliers | --- ## 5. Combined Loss (Production) ### Description Combines multiple loss functions for production models. ```python loss = α·MSE + β·Relative + γ·Physics + δ·MaxError ``` ### Implementation ```python def combined_loss(predicted, target, data, config): """ Production loss combining multiple objectives. """ losses = {} # MSE component losses['mse'] = mse_loss(predicted, target) # Relative component losses['relative'] = relative_loss(predicted, target) # Physics component losses['physics'] = physics_informed_loss(predicted, target, data, config) # Max error component losses['max'] = max_error_loss(predicted, target) # Weighted combination total = ( config.alpha * losses['mse'] + config.beta * losses['relative'] + config.gamma * losses['physics'] + config.delta * losses['max'] ) return total, losses ``` ### Recommended Weights | Application | MSE (α) | Relative (β) | Physics (γ) | Max (δ) | |-------------|---------|--------------|-------------|---------| | General | 0.5 | 0.3 | 0.2 | 0.0 | | Multi-scale | 0.2 | 0.5 | 0.2 | 0.1 | | Safety-critical | 0.2 | 0.2 | 0.3 | 0.3 | | Extrapolation | 0.2 | 0.2 | 0.5 | 0.1 | --- ## Configuration Examples ### Basic Training ```python # config.yaml loss: type: "mse" ``` ### Multi-Scale Outputs ```python # config.yaml loss: type: "combined" weights: mse: 0.2 relative: 0.5 physics: 0.2 max_error: 0.1 ``` ### Physics-Informed Training ```python # config.yaml loss: type: "physics_informed" physics_weight: 0.3 constraints: equilibrium: 0.3 constitutive: 0.1 boundary: 0.5 ``` ### Safety-Critical ```python # config.yaml loss: type: "combined" weights: mse: 0.2 relative: 0.2 physics: 0.3 max_error: 0.3 max_error_percentile: 99 ``` --- ## Training Strategies ### Curriculum Learning Start simple, add complexity: ```python def get_loss_weights(epoch, total_epochs): """Gradually increase physics loss weight""" progress = epoch / total_epochs if progress < 0.3: # Phase 1: Pure MSE return {'mse': 1.0, 'physics': 0.0} elif progress < 0.6: # Phase 2: Add physics physics_weight = (progress - 0.3) / 0.3 * 0.3 return {'mse': 1.0 - physics_weight, 'physics': physics_weight} else: # Phase 3: Full physics return {'mse': 0.7, 'physics': 0.3} ``` ### Adaptive Weighting Adjust weights based on loss magnitudes: ```python def adaptive_weights(losses): """Balance losses to similar magnitudes""" # Compute inverse of each loss (normalized) total = sum(losses.values()) weights = {k: total / (v + 1e-8) for k, v in losses.items()} # Normalize to sum to 1 weight_sum = sum(weights.values()) weights = {k: v / weight_sum for k, v in weights.items()} return weights ``` --- ## Troubleshooting ### Loss Not Decreasing **Symptom**: Training loss stays flat. **Solutions**: 1. Reduce learning rate 2. Check data normalization 3. Simplify loss (use MSE first) 4. Increase model capacity ### Physics Loss Dominates **Symptom**: Physics loss >> data loss. **Solutions**: 1. Reduce physics weight (λ) 2. Use curriculum learning 3. Check physics computation 4. Normalize constraints ### Unstable Training **Symptom**: Loss oscillates or explodes. **Solutions**: 1. Use gradient clipping 2. Reduce learning rate 3. Check for NaN in physics terms 4. Add epsilon to divisions --- ## Metrics for Evaluation ### Training Metrics ```python metrics = { 'train_loss': total_loss.item(), 'train_mse': losses['mse'].item(), 'train_physics': losses['physics'].item(), 'train_max': losses['max'].item() } ``` ### Validation Metrics ```python def compute_validation_metrics(model, val_loader): """Compute physics-aware validation metrics""" all_errors = [] physics_violations = [] for batch in val_loader: pred = model(batch) # Prediction errors errors = torch.abs(pred - batch.y) all_errors.append(errors) # Physics violations violations = compute_physics_residual(pred, batch) physics_violations.append(violations) return { 'val_mae': torch.cat(all_errors).mean(), 'val_max': torch.cat(all_errors).max(), 'val_physics_violation': torch.cat(physics_violations).mean(), 'val_physics_compliance': (torch.cat(physics_violations) < 0.01).float().mean() } ``` --- ## Summary | Situation | Recommended Loss | |-----------|-----------------| | Starting out | MSE | | Multi-scale outputs | Relative + MSE | | Need generalization | Physics-informed | | Safety-critical | Combined with max error | | Limited training data | Physics-informed | | Production deployment | Combined (tuned) | --- ## See Also - [Neural Features Complete](NEURAL_FEATURES_COMPLETE.md) - Overview - [GNN Architecture](GNN_ARCHITECTURE.md) - Model details - [Neural Workflow Tutorial](NEURAL_WORKFLOW_TUTORIAL.md) - Training guide