44import torch .nn as nn
55import torch .optim as optim
66import csv
7+ import pandas as pd
78import os
89
910from rbf_network import WendlandLinearNetwork
@@ -60,6 +61,48 @@ def generate_centers(num_centers):
6061 centers = np .vstack ([X .ravel (), Y .ravel ()]).T
6162 return torch .tensor (centers , dtype = torch .float32 )
6263
64+ def estimate_convergence_order (csv_filename ):
65+ """
66+ Opens a CSV file containing numerical convergence results and estimates the
67+ convergence order for each row using log-log error reduction.
68+
69+ The last row's convergence order is set equal to the second-to-last row.
70+
71+ Parameters:
72+ csv_filename (str): The path to the CSV file to process.
73+ """
74+ # Load the CSV file
75+ df = pd .read_csv (csv_filename )
76+
77+ # Ensure required columns exist
78+ required_columns = {"point_dist" , "err_validation" }
79+ if not required_columns .issubset (df .columns ):
80+ raise ValueError (f"Missing required columns in CSV: { required_columns - set (df .columns )} " )
81+
82+ # Compute convergence order using log-log slope formula
83+ convergence_orders = []
84+
85+ for i in range (len (df ) - 1 ): # Iterate up to the second-to-last row
86+ h_coarse , h_fine = df .iloc [i ]["point_dist" ], df .iloc [i + 1 ]["point_dist" ]
87+ err_coarse , err_fine = df .iloc [i ]["err_validation" ], df .iloc [i + 1 ]["err_validation" ]
88+
89+ if err_coarse > 0 and err_fine > 0 : # Avoid log errors due to zero or negative values
90+ p = np .log (err_coarse / err_fine ) / np .log (h_coarse / h_fine )
91+ convergence_orders .append (p )
92+ else :
93+ convergence_orders .append (np .nan )
94+
95+ # Ensure last row gets the same convergence order as the previous row
96+ convergence_orders .append (convergence_orders [- 1 ] if len (convergence_orders ) > 0 else np .nan )
97+
98+ # Add convergence order column
99+ df ["error_convergence_order" ] = convergence_orders
100+
101+ # Save the updated CSV file
102+ df .to_csv (csv_filename , index = False )
103+
104+ print (f"Updated { csv_filename } with convergence orders." )
105+
63106def main (num_points ):
64107 # Generate training data
65108 x = np .linspace (0 , 1 , num_points )
@@ -76,7 +119,7 @@ def main(num_points):
76119 # centers = generate_centers(32).clone().detach()
77120 centers = x_train
78121 print (centers .shape )
79- r_max = 3.0 / num_points
122+ r_max = 2.5 / num_points
80123 smoothness = 4 # C^4 smoothness
81124
82125 # Initialize model
@@ -87,7 +130,7 @@ def main(num_points):
87130 criterion = nn .MSELoss ()
88131
89132 # Training loop
90- epochs = 2000
133+ epochs = 4000
91134 for epoch in range (epochs ):
92135 model .train ()
93136 optimizer .zero_grad ()
@@ -119,15 +162,14 @@ def main(num_points):
119162
120163 # Reshape for visualization
121164 psi_pred = pred .reshape (X_val .shape )
122- #psi_actual = psi(X_val, Y_val)
123165
124166 # Visualize actual and predicted stream functions
125167 visualize_psi (X_val , Y_val , psi_val , centers , title = "Actual Stream Function" )
126168 visualize_psi (X_val , Y_val , psi_pred , centers , title = "Predicted Stream Function" )
127169
128- err_val = np .abs (psi_pred - psi_val )
170+ err_val = np .abs (psi_pred - psi_val ) / np . max ( psi_val )
129171 visualize_psi (X_val , Y_val , err_val , centers ,
130- title = "Stream Function Approximation Error" )
172+ title = "Stream Function Relative Approximation Error" )
131173
132174 # Define the filename
133175 csv_filename = "stream_function_validation.csv"
@@ -162,7 +204,10 @@ def main(num_points):
162204 title = "Predicted Velocity Field" )
163205
164206if __name__ == "__main__" :
165- main (num_points = 4 )
166- main (num_points = 8 )
167- main (num_points = 16 )
168- main (num_points = 32 )
207+
208+ # Run mesh convergence study
209+ for num_points in [4 ,8 ,16 ,32 ]:
210+ main (num_points )
211+
212+ # Estimate convergence order
213+ estimate_convergence_order ("stream_function_validation.csv" )
0 commit comments