@@ -189,15 +189,16 @@ def R_1n_vectorized(n, r, i, h, d, a):
189189 """
190190 Vectorized version of the R_1n radial eigenfunction.
191191 """
192- # --- FIX: Ensure inputs are FLOAT arrays to prevent dtype casting errors ---
192+ # --- FIX: Ensure inputs are FLOAT arrays to prevent casting errors on int arrays ---
193193 n = np .asarray (n , dtype = float )
194194 r = np .asarray (r , dtype = float )
195- # -------------------------------------
195+ # ---------------------------------------------------------------------------------
196196
197197 cond_n_is_zero = (n == 0 )
198198 cond_r_at_boundary = (r == scale (a )[i ])
199199
200200 # --- Define the outcomes for each condition ---
201+ # FIX: n=0 logic must match scalar version
201202 if i == 0 :
202203 outcome_for_n_zero = np .full_like (r , 0.5 )
203204 else :
@@ -207,15 +208,19 @@ def R_1n_vectorized(n, r, i, h, d, a):
207208 val = 1.0 + 0.5 * np .log (np .divide (r , scale (a )[i ]))
208209 outcome_for_n_zero = val
209210
211+ # Outcome 2: If n>=1 and r is at the boundary, the value is 1.0.
210212 outcome_for_r_boundary = 1.0
211213
212- # Calculate lambda. Mask n=0 to prevent divide by zero in Bessel if lambda becomes 0
214+ # Outcome 3: The general case for n>=1 inside the boundary.
213215 lambda_val = lambda_ni (n , i , h , d )
216+
217+ # Safety against divide by zero if n=0 (masked anyway)
214218 safe_lambda = np .where (cond_n_is_zero , 1.0 , lambda_val )
215219
216220 bessel_term = (besselie (0 , safe_lambda * r ) / besselie (0 , safe_lambda * scale (a )[i ])) * \
217221 exp (safe_lambda * (r - scale (a )[i ]))
218222
223+ # --- Apply the logic using nested np.where ---
219224 result_if_n_not_zero = np .where (cond_r_at_boundary , outcome_for_r_boundary , bessel_term )
220225
221226 return np .where (cond_n_is_zero , outcome_for_n_zero , result_if_n_not_zero )
@@ -228,15 +233,17 @@ def diff_R_1n_vectorized(n, r, i, h, d, a):
228233 # --- FIX: Ensure inputs are FLOAT arrays ---
229234 n = np .asarray (n , dtype = float )
230235 r = np .asarray (r , dtype = float )
231- # -------------------------------------
236+ # -------------------------------------------
232237
233238 condition = (n == 0 )
234239
235240 if i == 0 :
236241 value_if_true = np .zeros_like (r )
237242 else :
243+ # Derivative is 1/(2r)
238244 value_if_true = np .divide (1.0 , 2 * r , out = np .full_like (r , np .inf ), where = (r != 0 ))
239245
246+ # --- Calculation for when n > 0 ---
240247 lambda_val = lambda_ni (n , i , h , d )
241248 safe_lambda = np .where (condition , 1.0 , lambda_val )
242249
@@ -266,7 +273,7 @@ def R_2n_vectorized(n, r, i, a, h, d):
266273 # --- FIX: Ensure inputs are FLOAT arrays ---
267274 n = np .asarray (n , dtype = float )
268275 r = np .asarray (r , dtype = float )
269- # -------------------------------------
276+ # -------------------------------------------
270277
271278 # LEGACY: Use Outer Radius
272279 outer_r = scale (a )[i ]
@@ -302,7 +309,7 @@ def diff_R_2n_vectorized(n, r, i, h, d, a):
302309 # --- FIX: Ensure inputs are FLOAT arrays ---
303310 n = np .asarray (n , dtype = float )
304311 r = np .asarray (r , dtype = float )
305- # -------------------------------------
312+ # -------------------------------------------
306313
307314 # Case n == 0: Derivative is still 1/(2r)
308315 value_if_true = np .divide (1.0 , 2 * r , out = np .full_like (r , np .inf ), where = (r != 0 ))
@@ -335,7 +342,7 @@ def Z_n_i_vectorized(n, z, i, h, d):
335342 # --- FIX: Ensure inputs are FLOAT arrays ---
336343 n = np .asarray (n , dtype = float )
337344 z = np .asarray (z , dtype = float )
338- # -------------------------------------
345+ # -------------------------------------------
339346
340347 condition = (n == 0 )
341348
@@ -355,7 +362,7 @@ def diff_Z_n_i_vectorized(n, z, i, h, d):
355362 # --- FIX: Ensure inputs are FLOAT arrays ---
356363 n = np .asarray (n , dtype = float )
357364 z = np .asarray (z , dtype = float )
358- # -------------------------------------
365+ # -------------------------------------------
359366
360367 condition = (n == 0 )
361368 value_if_true = 0.0
@@ -376,31 +383,39 @@ def Lambda_k_vectorized(k, r, m0, a, m_k_arr):
376383 # --- FIX: Ensure inputs are FLOAT arrays ---
377384 k = np .asarray (k , dtype = float )
378385 r = np .asarray (r , dtype = float )
379- # -------------------------------------
380-
381- if m0 == inf :
382- return np .ones (np .broadcast (k , r ).shape , dtype = float )
386+ # -------------------------------------------
383387
384388 cond_k_is_zero = (k == 0 )
385389 cond_r_at_boundary = (r == scale (a )[- 1 ])
386390
387391 outcome_boundary = 1.0
388392
389393 # --- Case 2: k = 0 ---
390- denom_k_zero = besselh (0 , m0 * scale (a )[- 1 ])
391- numer_k_zero = besselh (0 , m0 * r )
392- with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
393- outcome_k_zero = np .divide (numer_k_zero , denom_k_zero ,
394- out = np .zeros_like (numer_k_zero , dtype = complex ),
395- where = np .isfinite (denom_k_zero ) & (denom_k_zero != 0 ))
394+ if m0 == inf :
395+ # Prevent Singularity: Return a dummy 1.0 to ensure matrix diagonal is non-zero
396+ # even if mode is physically decoupled (I_mk returns 0, so this 1.0 is multiplied by 0 in dense blocks)
397+ outcome_k_zero = np .ones_like (r , dtype = float )
398+ else :
399+ denom_k_zero = besselh (0 , m0 * scale (a )[- 1 ])
400+ numer_k_zero = besselh (0 , m0 * r )
401+ with np .errstate (divide = 'ignore' , invalid = 'ignore' ):
402+ outcome_k_zero = np .divide (numer_k_zero , denom_k_zero ,
403+ out = np .zeros_like (numer_k_zero , dtype = complex ),
404+ where = np .isfinite (denom_k_zero ) & (denom_k_zero != 0 ))
396405
397406 # --- Case 3: k > 0 ---
398- # Mask input where k=0 to prevent errors
399- # Note: k is float array, m_k_arr needs integer indexing if we index into it.
400- # However, k comes in as float from np.asarray. We must cast back to int for indexing.
407+ # Cast k back to int for indexing into m_k_arr
408+ # NOTE: m_k_arr contains valid finite values for k>0 even if m0=inf (see m_k_entry)
401409 k_int = k .astype (int )
410+
411+ # We must be careful not to index out of bounds if k contains invalid indices (though in context it shouldn't)
412+ # To be safe for vectorization, we mask the lookup.
413+ # However, k comes from NMK range, so it should be valid.
414+
415+ # Use 'safe' indexing (just use 0 where k=0 to avoid errors if m_k_arr has issues at 0, though m_k_arr[0] is handled)
402416 local_m_k_k = m_k_arr [k_int ]
403417
418+ # Mask input where k=0 (handled by outcome_k_zero)
404419 safe_m_k = np .where (cond_k_is_zero , 1.0 , local_m_k_k )
405420
406421 denom_k_nonzero = besselke (0 , safe_m_k * scale (a )[- 1 ])
@@ -427,24 +442,23 @@ def diff_Lambda_k_vectorized(k, r, m0, a, m_k_arr):
427442 # --- FIX: Ensure inputs are FLOAT arrays ---
428443 k = np .asarray (k , dtype = float )
429444 r = np .asarray (r , dtype = float )
430- # -------------------------------------
431-
432- # Handle the scalar case where m0 is infinite. The result is always 1.
433- if m0 == inf :
434- return np .ones (np .broadcast (k , r ).shape , dtype = float )
445+ # -------------------------------------------
435446
436447 # --- Define the condition for vectorization ---
437448 condition = (k == 0 )
438449
439450 # --- Define the outcome for k == 0 ---
440- numerator_k_zero = - (m0 * besselh (1 , m0 * r ))
441- denominator_k_zero = besselh (0 , m0 * scale (a )[- 1 ])
442- outcome_k_zero = np .divide (numerator_k_zero , denominator_k_zero ,
443- out = np .zeros_like (numerator_k_zero , dtype = complex ),
444- where = (denominator_k_zero != 0 ))
451+ if m0 == inf :
452+ # Prevent Singularity: Return 1.0 to ensure matrix pivot exists
453+ outcome_k_zero = np .ones_like (r , dtype = float )
454+ else :
455+ numerator_k_zero = - (m0 * besselh (1 , m0 * r ))
456+ denominator_k_zero = besselh (0 , m0 * scale (a )[- 1 ])
457+ outcome_k_zero = np .divide (numerator_k_zero , denominator_k_zero ,
458+ out = np .zeros_like (numerator_k_zero , dtype = complex ),
459+ where = (denominator_k_zero != 0 ))
445460
446461 # --- Define the outcome for k > 0 ---
447- # Cast k back to int for indexing
448462 k_int = k .astype (int )
449463 local_m_k_k = m_k_arr [k_int ]
450464
@@ -489,7 +503,7 @@ def Z_k_e_vectorized(k, z, m0, h, m_k_arr, N_k_arr):
489503 # --- FIX: Ensure inputs are FLOAT arrays ---
490504 k = np .asarray (k , dtype = float )
491505 z = np .asarray (z , dtype = float )
492- # -------------------------------------
506+ # -------------------------------------------
493507
494508 # This outer conditional is fine because it operates on scalar inputs.
495509 if m0 * h < M0_H_THRESH :
@@ -522,7 +536,7 @@ def diff_Z_k_e_vectorized(k, z, m0, h, m_k_arr, N_k_arr):
522536 # --- FIX: Ensure inputs are FLOAT arrays ---
523537 k = np .asarray (k , dtype = float )
524538 z = np .asarray (z , dtype = float )
525- # -------------------------------------
539+ # -------------------------------------------
526540
527541 # This outer conditional is fine because it operates on scalar inputs.
528542 if m0 * h < M0_H_THRESH :
0 commit comments