@@ -25,18 +25,27 @@ X = np.linspace(0, 1, len(Y))
2525{% endif % }{% endraw % }
2626
2727N = len (Y )
28- MARGIN_FRACTION = 0.10
29- MIN_REGION_POINTS = 10
28+ MARGIN_FRACTION = 0.10 # Exclude 10% from edges to avoid surface effects
29+ MIN_REGION_POINTS = 10 # Minimum points needed for linear fit
3030
3131
3232def detect_interface_position (y_data , x_data ):
33+ """Find interface position by locating maximum gradient in ESP profile.
34+
35+ Uses gradient smoothing to identify the steepest change, which indicates
36+ the interface between two materials.
37+ """
3338 n = len (y_data )
3439 margin = max (int (n * MARGIN_FRACTION ), MIN_REGION_POINTS )
3540 if 2 * margin >= n :
3641 return x_data [n // 2 ], n // 2
42+
43+ # Calculate smoothed gradient to find interface
3744 gradient = np .abs (np .gradient (y_data ))
3845 window = max (n // 20 , 3 )
3946 smoothed = np .convolve (gradient , np .ones (window ) / window , mode = "same" )
47+
48+ # Search for maximum gradient (interface) excluding margins
4049 search_start = margin
4150 search_end = n - margin
4251 search_region = smoothed [search_start :search_end ]
@@ -47,52 +56,78 @@ def detect_interface_position(y_data, x_data):
4756
4857
4958def get_bulk_regions (n_points , interface_idx ):
59+ """Determine bulk-like regions on left and right sides for linear fitting.
60+
61+ Ensures regions are far enough from interface and edges to capture
62+ representative bulk behavior.
63+ """
5064 margin = max (int (n_points * MARGIN_FRACTION ), MIN_REGION_POINTS // 2 )
65+
66+ # Left bulk region: between left margin and interface
5167 left_start = margin
5268 left_end = max (left_start + MIN_REGION_POINTS , interface_idx - margin )
5369 if left_end <= left_start :
5470 left_end = min (interface_idx , left_start + MIN_REGION_POINTS )
71+
72+ # Right bulk region: between interface and right margin
5573 right_start = min (interface_idx + margin , n_points - margin - MIN_REGION_POINTS )
5674 right_end = n_points - margin
5775 if right_end <= right_start :
5876 right_start = max (interface_idx , right_end - MIN_REGION_POINTS )
77+
78+ # Ensure bounds are valid
5979 left_start = max (0 , left_start )
6080 left_end = min (n_points , left_end )
6181 right_start = max (0 , right_start )
6282 right_end = min (n_points , right_end )
83+
84+ # Final fallback for edge cases
6385 if left_end <= left_start :
6486 left_start = 0
6587 left_end = min (MIN_REGION_POINTS , interface_idx )
6688 if right_end <= right_start :
6789 right_start = max (interface_idx , n_points - MIN_REGION_POINTS )
6890 right_end = n_points
91+
6992 return (left_start , left_end ), (right_start , right_end )
7093
7194
7295def fit_linear_region (x_data , y_data , start_idx , end_idx ):
96+ """Fit linear regression to ESP data in specified region.
97+
98+ Returns slope and intercept. For non-polar materials, slope ≈ 0.
99+ For polar materials, slope represents internal electric field.
100+ """
73101 start_idx = max (0 , int (start_idx ))
74102 end_idx = min (len (x_data ), int (end_idx ))
75103 if end_idx <= start_idx :
76104 end_idx = start_idx + MIN_REGION_POINTS
105+
77106 x_region = x_data [start_idx :end_idx ]
78107 y_region = y_data [start_idx :end_idx ]
108+
109+ # Fallback for insufficient data: return flat line at mean
79110 if len (x_region ) < 2 :
80111 return {"slope" : 0.0 , "intercept" : float (np .mean (y_data )), "r_squared" : 0.0 }
112+
81113 slope , intercept , r_value , _, _ = linregress (x_region , y_region )
82114 return {"slope" : slope , "intercept" : intercept , "r_squared" : r_value ** 2 }
83115
84116
117+ # Main calculation: fit lines to bulk regions and extrapolate to interface
85118interface_x , interface_idx = detect_interface_position (Y , X )
86119left_region , right_region = get_bulk_regions (N , interface_idx )
87120left_fit = fit_linear_region (X , Y , left_region [0 ], left_region [1 ])
88121right_fit = fit_linear_region (X , Y , right_region [0 ], right_region [1 ])
122+
123+ # Extrapolate fitted lines to interface position to get ESP values
89124left_value_at_interface = left_fit ["slope" ] * interface_x + left_fit ["intercept" ]
90125right_value_at_interface = right_fit ["slope" ] * interface_x + right_fit ["intercept" ]
91126
127+ # Output in same format as find_extrema.pyi for compatibility
92128result = {
93129 "minima" : [float (left_value_at_interface ), float (right_value_at_interface )],
94130 "maxima" : [],
95131}
96132
97133print (json .dumps (result , indent = 4 ))
98-
0 commit comments