Repository: CIRA-Pulsars-and-Transients-Group/vcsbeam
Pull request #71: Add new guards to check for NaNs and implement temporary fix for corrupted outputs
Author: login: bwmeyers
Created: 2025-04-28T06:19:32Z | Merged: 2025-04-29T04:45:03Z

Mostly addresses #70 


Diff:
diff --git a/src/calibration.c b/src/calibration.c
index afe4d2f..f46da59 100644
--- a/src/calibration.c
+++ b/src/calibration.c
@@ -558,18 +558,28 @@ void vmLoadOffringaSolution( vcsbeam_context *vm )
 
 //fprintf(stderr, "Dread (Interval = %d, ant = %d, Ch = %d): ", Interval, ant, Ch); fprintf_complex_matrix( stderr, Dread );
 
-            // If there are any nans, set them to zero instead
-            // Assume that if there are any nans in the Jones matrix, then
-            // EVERY element in the Jones matrix is a nan. Therefore, only need
-            // to check one element.
-            if (isnan(Dread[0].x))
+            // If there are any nans, set the Jones matrix to zeros instead.
+            // Check every element, rather than assuming that the real part
+            // of the first element (Dread[0].x) is representative of the whole.
+            if (
+                isnan(Dread[0].x) || 
+                isnan(Dread[1].x) || 
+                isnan(Dread[2].x) || 
+                isnan(Dread[3].x) || 
+                isnan(Dread[0].y) || 
+                isnan(Dread[1].y) || 
+                isnan(Dread[2].y) || 
+                isnan(Dread[3].y)
+                )
+            {
                 memset( Dread, 0, JONES_SIZE_BYTES );
+            }
 
             // Copy this matrix into every corresponding "voltage" fine channel
             for (vch = ch*interp_factor; vch < (ch + 1)*interp_factor; vch++)
             {
                 // Get the destination index
-                d_idx = D_IDX(ant,vch,0,0,vcs_nchan,nantpol);
+                d_idx = D_IDX(ant, vch, 0, 0, vcs_nchan, nantpol);
 
                 // Copy it across
                 cp2x2( Dread, &(vm->D[d_idx]) );
diff --git a/src/form_beam.cpp b/src/form_beam.cpp
index 4b42343..64bd623 100644
--- a/src/form_beam.cpp
+++ b/src/form_beam.cpp
@@ -9,6 +9,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <time.h>
+#include <math.h>
 
 #include "gpu_fft.hpp"
 #include "gpu_macros.h"
@@ -298,7 +299,9 @@ __global__ void vmBeamform_kernel(int nfine_chan,
                 // Calculate beamform products for each antenna, and then add them together
                 // Calculate the coherent beam (B = J*phi*D)
                 gpuDoubleComplex ex_tmp = gpuCmul( phi[PHI_IDX(p,ant,c,nant,nc)], Jv_Q[Jv_IDX(p,s,c,ant,ns,nc,nant)] );
+                if(isnan(ex_tmp.x) || isnan(ex_tmp.y)) continue;  // TODO: Bandaid guard solution - why should there be NaNs??
                 gpuDoubleComplex ey_tmp = gpuCmul( phi[PHI_IDX(p,ant,c,nant,nc)], Jv_P[Jv_IDX(p,s,c,ant,ns,nc,nant)] );
+                if(isnan(ey_tmp.x) || isnan(ey_tmp.y)) continue;  // TODO: Bandaid guard solution - why should there be NaNs??
                 ex = gpuCadd(ex, ex_tmp);
                 ey = gpuCadd(ey, ey_tmp);
                 Nxx = gpuCadd(Nxx, gpuCmul( ex_tmp, gpuConj(ex_tmp)));
diff --git a/src/primary_beam.c b/src/primary_beam.c
index 8c4f8d1..d74c0ea 100644
--- a/src/primary_beam.c
+++ b/src/primary_beam.c
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <time.h>
+#include <math.h>
 
 #include <mwalib.h>
 #include <mwa_hyperbeam.h>
@@ -100,7 +101,7 @@ void vmCalcB(
     for (config_idx = 0; config_idx < NCONFIGS; config_idx++)
         configs[config_idx] = NULL;
 
-    double * tempJones;
+    double *tempJones;
 
     // Normalise to zenith
     int zenith_norm = 1;
@@ -115,13 +116,13 @@ void vmCalcB(
 
     double az, za; // (az)imuth and (z)enith (a)ngle in radians
 
-    uint8_t iauOrder=0; //Boolean 0= don't set Jones matrix to be iau order but instead mwa order in CalcJones
+    uint8_t iauOrder = 0; //Boolean 0= don't set Jones matrix to be iau order but instead mwa order in CalcJones
 
-    uint32_t numAmps=16; //number of dipole gains used (16 or 32)
+    uint32_t numAmps = 16; //number of dipole gains used (16 or 32)
 
     int32_t errInt; //exit code integer for calcJones
 
-    double * arrayLatitudeRad=NULL;
+    double *arrayLatitudeRad = NULL;
 
     // Loop through the pointings and calculate the primary beam matrices
     for (p = 0; p < pb->npointings; p++)
@@ -151,40 +152,97 @@ void vmCalcB(
                 // Use the 'dead' configuration temporarily
                 config_idx = DEAD_CONFIG;
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
+                
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
+                }
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
+                {
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
+                }
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
             else if (configs[config_idx] == NULL) // Call Hyperbeam if this config hasn't been done yet
             {
                 // Get the calculated FEE Beam (using Hyperbeam)
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
 
-                // Apply the parallactic angle correction
-#ifdef DEBUG
-                if (config_idx == 0)
-                {
-                    fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
-                    fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
                 }
-#endif
-                mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
-#ifdef DEBUG
-                if (config_idx == 0)
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
                 {
-                    fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
                 }
-#endif
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
-
-            if (errInt !=0)
-            {             
-                handle_hyperbeam_error("Primary Beam",183, "fee_calc_jones");   
-                exit(EXIT_FAILURE);
+                
+            // Apply the parallactic angle correction, regardless of configuration
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+             }
+#endif
+             mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
             }
+#endif
 
             // Copy the answer into the B matrix (for this antenna)
+            // Since we do an operation on the matrix in this case, check again for NaNs
+            if (
+                isnan(configs[config_idx][0].x) || 
+                isnan(configs[config_idx][1].x) || 
+                isnan(configs[config_idx][2].x) || 
+                isnan(configs[config_idx][3].x) || 
+                isnan(configs[config_idx][0].y) || 
+                isnan(configs[config_idx][1].y) || 
+                isnan(configs[config_idx][2].y) || 
+                isnan(configs[config_idx][3].y)
+                )
+            {
+                printf("NaNs in Primary Beam Jones matrix after parallactic corrections!!?\n");
+                memset( configs[config_idx], 0, JONES_SIZE_BYTES );
+            }
             cp2x2( configs[config_idx], &(pb->B[PB_IDX(p, ant, 0, nant, npol)]) );
         }
     }
@@ -213,11 +271,12 @@ void vmCreatePrimaryBeam( vcsbeam_context *vm )
     vm->pb.B = (gpuDoubleComplex *)malloc( size );
 
     vm->pb.beam = NULL;
-    errInt= new_fee_beam( HYPERBEAM_HDF5, &vm->pb.beam );
-
+    errInt = new_fee_beam( HYPERBEAM_HDF5, &vm->pb.beam );
+    
+    // Check error code, exit if it's not 0
     if (errInt != 0)
     {
-        handle_hyperbeam_error("Primary Beam", 213, "new_fee_beam");
+        handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "new_fee_beam");
         exit(EXIT_FAILURE);
     }
 


Reviews:
user: login: bwmeyers
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T06:31:19Z
user: login: robotopia
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T08:19:09Z
user: login: robotopia
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T08:20:23Z
user: login: bwmeyers
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T09:54:18Z

Review comments:
diff_hunk: @@ -151,40 +151,97 @@ void vmCalcB(
                 // Use the 'dead' configuration temporarily
                 config_idx = DEAD_CONFIG;
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
+                
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
+                }
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
+                {
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
+                }
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
             else if (configs[config_idx] == NULL) // Call Hyperbeam if this config hasn't been done yet
             {
                 // Get the calculated FEE Beam (using Hyperbeam)
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
 
-                // Apply the parallactic angle correction
-#ifdef DEBUG
-                if (config_idx == 0)
-                {
-                    fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
-                    fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
                 }
-#endif
-                mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
-#ifdef DEBUG
-                if (config_idx == 0)
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
                 {
-                    fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
                 }
-#endif
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
-
-            if (errInt !=0)
-            {             
-                handle_hyperbeam_error("Primary Beam",183, "fee_calc_jones");   
-                exit(EXIT_FAILURE);
+                
+            // Apply the parallactic angle correction, regardless of configuration
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+             }
+#endif
+             mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
             }
+#endif
 
             // Copy the answer into the B matrix (for this antenna)
+            // Since we do an operation on the matrix in this case, check again for NaNs
+            if (
+                isnan(configs[config_idx][0].x) || 
+                isnan(configs[config_idx][1].x) || 
+                isnan(configs[config_idx][2].x) || 
+                isnan(configs[config_idx][3].x) || 
+                isnan(configs[config_idx][0].y) || 
+                isnan(configs[config_idx][1].y) || 
+                isnan(configs[config_idx][2].y) || 
+                isnan(configs[config_idx][3].y)
+                )
+            {
+                printf("NaNs in Primary Beam Jones matrix after parallactic corrections!!?\n");
+                memset( configs[config_idx], 0, JONES_SIZE_BYTES );
path: src/primary_beam.c
user: login: bwmeyers
body: Actually is this okay? Or do we need to `memset` to `0` and then re-typecast? 
created_at: 2025-04-28T06:31:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -151,40 +151,97 @@ void vmCalcB(
                 // Use the 'dead' configuration temporarily
                 config_idx = DEAD_CONFIG;
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
+                
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
+                }
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
+                {
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
+                }
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
             else if (configs[config_idx] == NULL) // Call Hyperbeam if this config hasn't been done yet
             {
                 // Get the calculated FEE Beam (using Hyperbeam)
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
 
-                // Apply the parallactic angle correction
-#ifdef DEBUG
-                if (config_idx == 0)
-                {
-                    fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
-                    fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
                 }
-#endif
-                mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
-#ifdef DEBUG
-                if (config_idx == 0)
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
                 {
-                    fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
                 }
-#endif
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
-
-            if (errInt !=0)
-            {             
-                handle_hyperbeam_error("Primary Beam",183, "fee_calc_jones");   
-                exit(EXIT_FAILURE);
+                
+            // Apply the parallactic angle correction, regardless of configuration
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+             }
+#endif
+             mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
             }
+#endif
 
             // Copy the answer into the B matrix (for this antenna)
+            // Since we do an operation on the matrix in this case, check again for NaNs
+            if (
+                isnan(configs[config_idx][0].x) || 
+                isnan(configs[config_idx][1].x) || 
+                isnan(configs[config_idx][2].x) || 
+                isnan(configs[config_idx][3].x) || 
+                isnan(configs[config_idx][0].y) || 
+                isnan(configs[config_idx][1].y) || 
+                isnan(configs[config_idx][2].y) || 
+                isnan(configs[config_idx][3].y)
+                )
+            {
+                printf("NaNs in Primary Beam Jones matrix after parallactic corrections!!?\n");
+                memset( configs[config_idx], 0, JONES_SIZE_BYTES );
path: src/primary_beam.c
user: login: robotopia
body: I'm not 100% sure what the question means, but let me know if this answers it:

It's fine. `memset` is "low-level" enough that it takes in a pointer of any kind and just dumps the value you give it at that location in memory. It doesn't care about what "type" of pointer you give it, and it also doesn't affect the type of that pointer in any way.
created_at: 2025-04-28T08:19:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -151,40 +151,97 @@ void vmCalcB(
                 // Use the 'dead' configuration temporarily
                 config_idx = DEAD_CONFIG;
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
+                
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
+                }
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
+                {
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
+                }
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
             else if (configs[config_idx] == NULL) // Call Hyperbeam if this config hasn't been done yet
             {
                 // Get the calculated FEE Beam (using Hyperbeam)
                 tempJones = malloc(8*sizeof(double));
-                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input],numAmps, zenith_norm, arrayLatitudeRad ,iauOrder , tempJones);
-                configs[config_idx]=(gpuDoubleComplex *)(tempJones);
+                errInt = fee_calc_jones(pb->beam, az, za, pb->freq_hz, pb->delays[rf_input], pb->amps[rf_input], numAmps, zenith_norm, arrayLatitudeRad, iauOrder, tempJones);
 
-                // Apply the parallactic angle correction
-#ifdef DEBUG
-                if (config_idx == 0)
-                {
-                    fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
-                    fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+                // Check error code, exit if it's not 0
+                if (errInt != 0)
+                {             
+                    handle_hyperbeam_error(__FILE__, (__LINE__ - 5), "fee_calc_jones");   
+                    exit(EXIT_FAILURE);
                 }
-#endif
-                mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
-#ifdef DEBUG
-                if (config_idx == 0)
+                
+                // Check elements and emit a warning if there are NaNs in the primary beam output.
+                // If NaNs are detected, set the Jones matrix to zero.
+                if (
+                    isnan(tempJones[0]) || 
+                    isnan(tempJones[1]) || 
+                    isnan(tempJones[2]) || 
+                    isnan(tempJones[3]) || 
+                    isnan(tempJones[4]) || 
+                    isnan(tempJones[5]) || 
+                    isnan(tempJones[6]) || 
+                    isnan(tempJones[7]) 
+                    )
                 {
-                    fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                    printf("NaNs in Primary Beam Jones matrix!!?\n");
+                    memset( tempJones, 0, JONES_SIZE_BYTES );
                 }
-#endif
+                configs[config_idx] = (gpuDoubleComplex *)(tempJones);
             }
-
-            if (errInt !=0)
-            {             
-                handle_hyperbeam_error("Primary Beam",183, "fee_calc_jones");   
-                exit(EXIT_FAILURE);
+                
+            // Apply the parallactic angle correction, regardless of configuration
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "Bhb     = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
+                fprintf( stderr, "Ppa     = " ); fprintf( stderr, "[ %lf, %lf; %lf, %lf ]\n", P[0], P[1], P[2], P[3]  );
+             }
+#endif
+             mult2x2d_CxR( configs[config_idx], P, configs[config_idx] );
+#ifdef DEBUG
+            if (config_idx == 0)
+            {
+                fprintf( stderr, "B       = " ); fprintf_complex_matrix( stderr, configs[config_idx] );
             }
+#endif
 
             // Copy the answer into the B matrix (for this antenna)
+            // Since we do an operation on the matrix in this case, check again for NaNs
+            if (
+                isnan(configs[config_idx][0].x) || 
+                isnan(configs[config_idx][1].x) || 
+                isnan(configs[config_idx][2].x) || 
+                isnan(configs[config_idx][3].x) || 
+                isnan(configs[config_idx][0].y) || 
+                isnan(configs[config_idx][1].y) || 
+                isnan(configs[config_idx][2].y) || 
+                isnan(configs[config_idx][3].y)
+                )
+            {
+                printf("NaNs in Primary Beam Jones matrix after parallactic corrections!!?\n");
+                memset( configs[config_idx], 0, JONES_SIZE_BYTES );
path: src/primary_beam.c
user: login: bwmeyers
body: This does indeed answer my question. I was 85% sure it was fine, but given I'm only a mediocre C guy, thought I'd check. Thanks! 
created_at: 2025-04-28T09:54:17Z
author_association: CONTRIBUTOR

PR comments:
user: login: bwmeyers
created_at: 2025-04-28T06:39:41Z
body: Also I realise that I wasn't careful about checking that `math.h` is included... perhaps I need to explicitly do that, otherwise `isnan` is not defined... unless it's always added somewhere along the include chain... 

_Answering my own question: it's included as part of `vcsbeam.h` so I should actually remove that include from `calibration.c`_
author_association: CONTRIBUTOR
user: login: robotopia
created_at: 2025-04-28T07:39:30Z
body: > Also I realise that I wasn't careful about checking that `math.h` is included... perhaps I need to explicitly do that, otherwise `isnan` is not defined... unless it's always added somewhere along the include chain...
>
> _Answering my own question: it's included as part of `vcsbeam.h` so I should actually remove that include from `calibration.c`_

Actually, IMO, if you use a function from a library in a given source file, you should always explicitly `#include` the appropriate header in the same source file. The inclusion of that library in `vcsbeam.h` isn't so much to make the inclusion filter down to any source file that includes `vcsbeam.h`, but rather because there are symbols used in `vcsbeam.h` itself that are defined in `math.h`. If someone went through and removed such symbols from `vcsbeam.h`, they should feel able to also remove the `#include <math.h>` without worrying whether other source files that depend on `vcsbeam.h` would be able to be compiled or not.
author_association: CONTRIBUTOR
user: login: straten
created_at: 2025-04-28T08:51:21Z
body: Hi gang, note that `isnan` (and related `ininf`) can be disabled by compiler optimizations like `-ffast-math`; e.g. https://stackoverflow.com/questions/69463347/why-does-gcc-ffast-math-disable-the-correct-result-of-isnan-and-isinf

To work around this, for example, epsic defines it's own versions of these functions; e.g.

https://github.com/straten/epsic/blob/master/src/util/true_math.h
https://github.com/straten/epsic/blob/master/src/util/true_math.c

The key to making this work is the `#pragma GCC optimize ("no-fast-math")` at the top of true_math.c
author_association: COLLABORATOR
user: login: bwmeyers
created_at: 2025-04-28T09:56:33Z
body: > > Also I realise that I wasn't careful about checking that `math.h` is included... perhaps I need to explicitly do that, otherwise `isnan` is not defined... unless it's always added somewhere along the include chain...
> > _Answering my own question: it's included as part of `vcsbeam.h` so I should actually remove that include from `calibration.c`_
> 
> Actually, IMO, if you use a function from a library in a given source file, you should always explicitly `#include` the appropriate header in the same source file. The inclusion of that library in `vcsbeam.h` isn't so much to make the inclusion filter down to any source file that includes `vcsbeam.h`, but rather because there are symbols used in `vcsbeam.h` itself that are defined in `math.h`. If someone went through and removed such symbols from `vcsbeam.h`, they should feel able to also remove the `#include <math.h>` without worrying whether other source files that depend on `vcsbeam.h` would be able to be compiled or not.

Fair enough! That makes sense now that you say it that way. I will make sure that those source files also `#include <math.h>`. 
author_association: CONTRIBUTOR
user: login: bwmeyers
created_at: 2025-04-28T10:10:30Z
body: > Hi gang, note that `isnan` (and related `ininf`) can be disabled by compiler optimizations like `-ffast-math`; e.g. https://stackoverflow.com/questions/69463347/why-does-gcc-ffast-math-disable-the-correct-result-of-isnan-and-isinf
> 
> To work around this, for example, epsic defines it's own versions of these functions; e.g.
> 
> https://github.com/straten/epsic/blob/master/src/util/true_math.h https://github.com/straten/epsic/blob/master/src/util/true_math.c
> 
> The key to making this work is the `#pragma GCC optimize ("no-fast-math")` at the top of true_math.c

That's super interesting, thanks @straten! Certainly in our "Release" version when compiled, we set `-O3` but it looks like that doesn't invoke `-ffast-math` (which in turn would invoke `-ffinite-math-only` and `-fno-signaling-nans`), so the `isnan()` behaviour should be well-defined... I think?
author_association: CONTRIBUTOR
user: login: bwmeyers
created_at: 2025-04-29T01:03:37Z
body: I don't know why those checks are failing. It says due to `math.h` not being found, which I find suspicious given it's a standard library thing...
author_association: CONTRIBUTOR
user: login: bwmeyers
created_at: 2025-05-21T01:51:29Z
body: FWIW, these changes definitely broke things as we can't detect pulsars with this specific version any more... yikes!  I've removed the Release (v5.2.1). 
author_association: CONTRIBUTOR