diff --git a/Examples/Bfield/B.c b/Examples/Bfield/B.c index f8ea50cdc..5dafe1bfb 100644 --- a/Examples/Bfield/B.c +++ b/Examples/Bfield/B.c @@ -35,13 +35,14 @@ int main(){ * Or there is a setter routine ... */ //Lgm_MagModelInfo_Set_MagModel( LGM_EDIP, LGM_EXTMODEL_T89, mInfo ); + Lgm_MagModelInfo_Set_MagModel( LGM_EDIP, LGM_EXTMODEL_TS04, mInfo ); /* * For TS07, the coeffs need to be initialized for each new time... */ - Lgm_SetCoeffs_TS07( Date, UTC, &mInfo->TS07_Info ); + //Lgm_SetCoeffs_TS07( Date, UTC, &mInfo->TS07_Info ); @@ -83,7 +84,7 @@ int main(){ for (i=0; i<=5; i++) { mInfo->Kp = i; - u.x = -6.6; u.y = 0.0; u.z = 0.0; + u.x = -5.8; u.y = 0.0; u.z = 0.0; //Lgm_Convert_Coords( &u, &ugsm, GEO_TO_GSM, mInfo->c ); Lgm_Convert_Coords( &u, &ugsm, SM_TO_GSM, mInfo->c ); mInfo->Bfield( &ugsm, &B, mInfo ); @@ -112,6 +113,35 @@ int main(){ + double Tilt, Phi, Theta, MLT, R; + R = 6.6; + FILE *fp1 = fopen( "Tilt1.txt", "w" ); + FILE *fp2 = fopen( "Tilt2.txt", "w" ); + for (MLT = 18.0; MLT <= 30.0; MLT += 0.1 ) { + + Phi = 180.0 + (MLT-18.0)*15.0; + Theta = 0.0; + + u.x = R * cos( Phi*RadPerDeg ) * cos( Theta *RadPerDeg ); + u.y = R * sin( Phi*RadPerDeg ) * cos( Theta *RadPerDeg ); + u.z = R * sin( Theta *RadPerDeg ); +printf("u = %g %g %g\n", u.x, u.y, u.z); + + Lgm_Convert_Coords( &u, &ugsm, SM_TO_GSM, mInfo->c ); + + mInfo->Bfield( &ugsm, &B, mInfo ); + + Tilt = DegPerRad * atan2( B.z, sqrt(B.x*B.x + B.y*B.y) ); + fprintf( fp1, "%g %g\n", MLT, Tilt ); + + + Tilt = DegPerRad * asin( B.z/sqrt(B.x*B.x + B.y*B.y + B.z*B.z) ); + fprintf( fp2, "%g %g\n", MLT, Tilt ); + + } + fclose(fp1); + fclose(fp2); + diff --git a/Examples/Bfield/GradBvec.c b/Examples/Bfield/GradBvec.c new file mode 100644 index 000000000..fee2b8f6c --- /dev/null +++ b/Examples/Bfield/GradBvec.c @@ -0,0 +1,106 @@ +#include + +/* BAL 04-Jan-2011 modified for more cases */ + +int main(){ + + + long int Date; + double UTC; + double GradBvec[3][3]; + Lgm_Vector u, B, CurlB, GradB; + Lgm_MagModelInfo *mInfo; + int i, j; + + + Date = 20050831; + UTC = 9.0; + + mInfo = Lgm_InitMagInfo( ); + Lgm_Set_Coord_Transforms( Date, UTC, mInfo->c ); + + //mInfo->Bfield = Lgm_B_T89; + //mInfo->Bfield = Lgm_B_TS04; + //mInfo->Bfield = Lgm_B_OP77; + //mInfo->Bfield = Lgm_B_T89; + mInfo->Bfield = Lgm_B_TS04; + mInfo->P = 4.1011111111111118; + mInfo->Dst = 7.7777777777777777; + mInfo->By = 3.7244444444444444; + mInfo->Bz = -0.12666666666666665; + mInfo->W[0] = 0.12244444444444445; + mInfo->W[1] = 0.2514; + mInfo->W[2] = 0.089266666666666661; + mInfo->W[3] = 0.047866666666666668; + mInfo->W[4] = 0.22586666666666666; + mInfo->W[5] = 1.0461333333333334; + + + printf("%5s", "Kp"); + printf(" %10s", "Ux (Re)"); + printf(" %10s", "Uy (Re)"); + printf(" %10s", "Uz (Re)"); + printf(" %10s", "Bx (nT)"); + printf(" %10s", "By (nT)"); + printf(" %10s", "Bz (nT)"); + printf(" %10s", "Bmag (nT)"); + printf(" %15s", "(\u2207B)_x"); + printf(" %15s", "(\u2207B)_y"); + printf(" %15s", "(\u2207B)_z\n"); + + + for (i=0; i<=5; i++) { + mInfo->Kp = i; + u.x = -6.6; u.y = 0.0; u.z = 0.0; + mInfo->Bfield( &u, &B, mInfo ); + Lgm_CurlB( &u, &CurlB, LGM_DERIV_SIX_POINT, 1e-3, mInfo ); + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-3, mInfo ); + Lgm_GradBvec( &u, GradBvec, LGM_DERIV_SIX_POINT, 1e-3, mInfo ); + + printf("\n\n"); + printf( "\t Kp = %5d\n", mInfo->Kp); + printf( "\t u = %10g %10g %10g\n", u.x, u.y, u.z ); + printf( "\t B = %10g %10g %10g\n", B.x, B.y, B.z ); + printf( "\t |B| = %10g\n", Lgm_Magnitude( &B ) ); + printf( "\t CurlB = %15g %15g %15g\n", CurlB.x, CurlB.y, CurlB.z ); + printf( "\t GradB = %15g %15g %15g\n", GradB.x, GradB.y, GradB.z ); + printf( "\t (%8g %8g %8g)\n", GradBvec[0][0], GradBvec[0][1], GradBvec[0][2] ); + printf( "\t Grad Bvec = (%8g %8g %8g)\n", GradBvec[1][0], GradBvec[1][1], GradBvec[1][2] ); + printf( "\t (%8g %8g %8g)\n", GradBvec[2][0], GradBvec[2][1], GradBvec[2][2] ); + double Bmag = Lgm_Magnitude( &B ); + printf("\n\t Show that GradB can be derived from GradBvec...\n"); + printf("\t GradB_X: %g\n", (B.x*GradBvec[0][0] + B.y*GradBvec[1][0] + B.z*GradBvec[2][0])/Bmag ); + printf("\t GradB_Y: %g\n", (B.x*GradBvec[0][1] + B.y*GradBvec[1][1] + B.z*GradBvec[2][1])/Bmag ); + printf("\t GradB_Z: %g\n", (B.x*GradBvec[0][2] + B.y*GradBvec[1][2] + B.z*GradBvec[2][2])/Bmag ); + printf("\n\t Show that CurlB can be derived from GradBvec...\n"); + printf("\t CurlB_X: %g\n", GradBvec[2][1] - GradBvec[1][2] ); + printf("\t CurlB_Y: %g\n", GradBvec[0][2] - GradBvec[2][0] ); + printf("\t CurlB_Z: %g\n", GradBvec[1][0] - GradBvec[0][1] ); + } + +/* + for (j=0; j<100; j++){ + mInfo->Kp = 3; + for (i=0; i<13; i++) { + u.x = -1. - (double)i * 0.5; + u.y = 0.0; u.z = 0.0; + mInfo->Bfield( &u, &B, mInfo ); + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-3, mInfo ); + printf( "%5d", mInfo->Kp); + printf( " %10g %10g %10g", u.x, u.y, u.z ); + printf( " %10g %10g %10g", B.x, B.y, B.z ); + printf( " %10g", Lgm_Magnitude( &B ) ); + printf( " %15g %15g %15g \n", GradB.x, GradB.y, GradB.z ); + } + } +*/ + + + + + + Lgm_FreeMagInfo( mInfo ); + + + exit(0); +} diff --git a/Examples/Bfield/GradBvec2.c b/Examples/Bfield/GradBvec2.c new file mode 100644 index 000000000..6050c5b40 --- /dev/null +++ b/Examples/Bfield/GradBvec2.c @@ -0,0 +1,115 @@ +#include +#include + +/* BAL 04-Jan-2011 modified for more cases */ + +int main(){ + + + long int Date; + double UTC; + double GradBvec[4][4]; + Lgm_Vector u, B, CurlB, GradB; + Lgm_MagModelInfo **mInfo; + int i, j, n; + + j = 167; + n = 8640; + LGM_ARRAY_1D( mInfo, n, Lgm_MagModelInfo * ); + + /* + * Initialize an array of mInfo[i] structures... + */ + Date = 20050831; + for ( i=0; i<8640; ++i ) { + UTC = (double)i*10.0/3600.0; + mInfo[i] = Lgm_InitMagInfo( ); + mInfo[i]->Kp = i%5; + Lgm_Set_Coord_Transforms( Date, UTC, mInfo[i]->c ); + //printf("%8ldT%02d%02d%02d %g\n", mInfo[i]->c->UTC.Date, mInfo[i]->c->UTC.Hour, mInfo[i]->c->UTC.Minute, (int)mInfo[i]->c->UTC.Second, mInfo[i]->c->psi*DegPerRad ); + Lgm_MagModelInfo_Set_MagModel( LGM_IGRF, LGM_EXTMODEL_T89, mInfo[i] ); + mInfo[i]->P = 4.1011111111111118; + mInfo[i]->Dst = 7.7777777777777777; + mInfo[i]->By = 3.7244444444444444; + mInfo[i]->Bz = -0.12666666666666665; + mInfo[i]->W[0] = 0.12244444444444445; + mInfo[i]->W[1] = 0.2514; + mInfo[i]->W[2] = 0.089266666666666661; + mInfo[i]->W[3] = 0.047866666666666668; + mInfo[i]->W[4] = 0.22586666666666666; + mInfo[i]->W[5] = 1.0461333333333334; + } + + + printf("%5s", "Kp"); + printf(" %10s", "Ux (Re)"); + printf(" %10s", "Uy (Re)"); + printf(" %10s", "Uz (Re)"); + printf(" %10s", "Bx (nT)"); + printf(" %10s", "By (nT)"); + printf(" %10s", "Bz (nT)"); + printf(" %10s", "Bmag (nT)"); + printf(" %15s", "(\u2207B)_x"); + printf(" %15s", "(\u2207B)_y"); + printf(" %15s", "(\u2207B)_z\n"); + + + for (i=0; i<=5; i++) { + u.x = -6.6; u.y = 0.3; u.z = -2.0; + mInfo[j]->Bfield( &u, &B, mInfo[j] ); + Lgm_CurlB( &u, &CurlB, LGM_DERIV_SIX_POINT, 1e-3, mInfo[j] ); + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-3, mInfo[j] ); + Lgm_GradBvec2( j, &u, GradBvec, LGM_DERIV_SIX_POINT, 1e-3, n, 10.0, &mInfo[0] ); + + printf("\n\n"); + printf( "\t Kp = %5d\n", mInfo[i]->Kp); + printf( "\t u = %10g %10g %10g\n", u.x, u.y, u.z ); + printf( "\t B = %10g %10g %10g\n", B.x, B.y, B.z ); + printf( "\t |B| = %10g\n", Lgm_Magnitude( &B ) ); + printf( "\t CurlB = %15g %15g %15g\n", CurlB.x, CurlB.y, CurlB.z ); + printf( "\t GradB = %15g %15g %15g\n", GradB.x, GradB.y, GradB.z ); + printf( "\t (%8g %8g %8g %8g)\n", GradBvec[0][0], GradBvec[0][1], GradBvec[0][2], GradBvec[0][3] ); + printf( "\t Grad Bvec = (%8g %8g %8g %8g)\n", GradBvec[1][0], GradBvec[1][1], GradBvec[1][2], GradBvec[1][3] ); + printf( "\t (%8g %8g %8g %8g)\n", GradBvec[2][0], GradBvec[2][1], GradBvec[2][2], GradBvec[2][3] ); + printf( "\t (%8g %8g %8g %8g)\n", GradBvec[3][0], GradBvec[3][1], GradBvec[3][2], GradBvec[3][3] ); + double Bmag = Lgm_Magnitude( &B ); + printf("\n\t Show that GradB can be derived from GradBvec...\n"); + printf("\t GradB_T: %g\n", (B.x*GradBvec[1][0] + B.y*GradBvec[2][0] + B.z*GradBvec[3][0])/Bmag ); + printf("\t GradB_X: %g\n", (B.x*GradBvec[1][1] + B.y*GradBvec[2][1] + B.z*GradBvec[3][1])/Bmag ); + printf("\t GradB_Y: %g\n", (B.x*GradBvec[1][2] + B.y*GradBvec[2][2] + B.z*GradBvec[3][2])/Bmag ); + printf("\t GradB_Z: %g\n", (B.x*GradBvec[1][3] + B.y*GradBvec[2][3] + B.z*GradBvec[3][3])/Bmag ); + printf("\n\t Show that CurlB can be derived from GradBvec...\n"); + printf("\t CurlB_X: %g\n", GradBvec[3][2] - GradBvec[2][3] ); + printf("\t CurlB_Y: %g\n", GradBvec[1][3] - GradBvec[3][1] ); + printf("\t CurlB_Z: %g\n", GradBvec[2][1] - GradBvec[1][2] ); + } + +/* + for (j=0; j<100; j++){ + mInfo[i]->Kp = 3; + for (i=0; i<13; i++) { + u.x = -1. - (double)i * 0.5; + u.y = 0.0; u.z = 0.0; + mInfo[i]->Bfield( &u, &B, mInfo[i] ); + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-3, mInfo[i] ); + printf( "%5d", mInfo[i]->Kp); + printf( " %10g %10g %10g", u.x, u.y, u.z ); + printf( " %10g %10g %10g", B.x, B.y, B.z ); + printf( " %10g", Lgm_Magnitude( &B ) ); + printf( " %15g %15g %15g \n", GradB.x, GradB.y, GradB.z ); + } + } +*/ + + + + + + for ( i=0; i<8640; ++i ) { + Lgm_FreeMagInfo( mInfo[i] ); + } + LGM_ARRAY_1D_FREE( mInfo ); + + + exit(0); +} diff --git a/Examples/Bfield/Makefile b/Examples/Bfield/Makefile index e610547e3..701e3110d 100644 --- a/Examples/Bfield/Makefile +++ b/Examples/Bfield/Makefile @@ -2,7 +2,7 @@ HDF5FLAGS = `pkg-config hdf5 --cflags --libs 2>/dev/null` LGMFLAGS = `pkg-config lgm --cflags --libs` -all: B B2 GradB GradB2 B_Cross_GradB_Over_B CurlB CurlB2 DivB +all: B B2 GradBvec GradBvec2 GradB GradB2 B_Cross_GradB_Over_B CurlB CurlB2 DivB B: B.c @@ -11,6 +11,12 @@ B: B.c B2: B2.c gcc B2.c $(LGMFLAGS) $(HDF5FLAGS) -o B2 -Wall +GradBvec: GradBvec.c + gcc GradBvec.c $(LGMFLAGS) $(HDF5FLAGS) -o GradBvec -Wall + +GradBvec2: GradBvec2.c + gcc GradBvec2.c $(LGMFLAGS) $(HDF5FLAGS) -o GradBvec2 -Wall + GradB: GradB.c gcc GradB.c $(LGMFLAGS) $(HDF5FLAGS) -o GradB -Wall @@ -31,4 +37,4 @@ DivB: DivB.c clean: - rm *~ *.o B B2 GradB GradB2 B_Cross_GradB_Over_B CurlB CurlB2 DivB + rm *~ *.o B B2 GradBvec GradBvec2 GradB GradB2 B_Cross_GradB_Over_B CurlB CurlB2 DivB diff --git a/Examples/Geodetic/Geodetic2.c b/Examples/Geodetic/Geodetic2.c new file mode 100644 index 000000000..16eb8f503 --- /dev/null +++ b/Examples/Geodetic/Geodetic2.c @@ -0,0 +1,39 @@ +// Geodetic.c +#include +int main(){ + + double GeodLat, GeodLong, GeodHeight, r; + double GeoLat, GeoLong, GeoHeight; + Lgm_Vector v; + + GeodLat = 35.0 + 53.0/60.0 + 17.0/3600.0; // degrees + GeodLong = -106.0 - 18.0/60.0 - 23.0/3600.0; // degrees + GeodHeight = 20.0; // km +GeoLat = 56.02; // degrees +GeoLong = -141.42; // degrees +GeoHeight = 850.0; // km +v.x = (1.0+GeoHeight/WGS84_A)*cos( GeoLong * RadPerDeg )*cos( GeoLat*RadPerDeg ); +v.y = (1.0+GeoHeight/WGS84_A)*sin( GeoLong * RadPerDeg )*cos( GeoLat*RadPerDeg ); +v.z = (1.0+GeoHeight/WGS84_A)*sin( GeoLat*RadPerDeg ); + Lgm_WGS84_to_GEOD( &v, &GeodLat, &GeodLong, &GeodHeight ); + +// Lgm_GEOD_to_WGS84( GeodLat, GeodLong, GeodHeight, &v ); + + printf( " Geodetic Position\n"); + printf( " ===================================\n"); + printf( " GeodLat : %lf degrees\n", GeodLat ); + printf( " GeodLong : %lf degrees\n", GeodLong ); + printf( " GeodHeight : %lf km\n\n", GeodHeight ); + + printf( " Geocentric Position\n"); + printf( " ===================================\n"); + printf( " X : %-15lf meters\n", v.x*WGS84_A*1000.0); // meters + printf( " Y : %-15lf meters\n", v.y*WGS84_A*1000.0); // meters + printf( " Z : %-15lf meters\n", v.z*WGS84_A*1000.0); // meters + r = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + printf( " Radius : %lf Re (%lf km)\n", r, r*WGS84_A ); + printf( " Latitude : %lf degrees\n", DegPerRad*asin( v.z/r ) ); + printf( " Longitude : %lf degrees\n\n", DegPerRad*atan2( v.y, v.x ) ); + + exit(0); +} diff --git a/Examples/MagEphem/Run_MagEphemFromFile.c b/Examples/MagEphem/Run_MagEphemFromFile.c new file mode 100644 index 000000000..03561c3c3 --- /dev/null +++ b/Examples/MagEphem/Run_MagEphemFromFile.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +const char *ProgramName = "MagEphemFromFile"; +const char *argp_program_version = "1.0.0"; +const char *argp_program_bug_address = ""; +static char doc[] = "Runs MagEphemFromFile over range of dates in an openmp loop"; + + + + +void StringSplit( char *Str, char *StrArray[], int len, int *n ); + + +/* + * Description of options accepted. The fields are as follows; + * + * { NAME, KEY, ARG, FLAGS, DOC } where each of these have the following + * meaning; + * NAME - Name of option's long argument (can be zero). + * KEY - Character used as key for the parser and it's short name. + * ARG - Name of the option's argument (zero if there isnt one). + * FLAGS - OPTION_ARG_OPTIONAL, OPTION_ALIAS, OPTION_HIDDEN, OPTION_DOC, + * or OPTION_NO_USAGE + */ +static struct argp_option Options[] = { + {"StartDateTime", 'S', "yyyymmdd[Thh:mm:ss]", 0, "Start date/time in ISO 8601 format. Seconds will be truncated to integers.", 0 }, + {"EndDateTime", 'E', "yyyymmdd[Thh:mm:ss]", 0, "End date/time in ISO 8601 format. Seconds will be truncated to integers.", 0 }, + {"Birds", 'b', "\"bird1,bird2,etc\"", 0, "Birds (sats) to use. E.g., \"ns55,ns70\"." }, + + { 0 } +}; + +//Mandatory arguments +#define nArgs 0 +static char ArgsDoc[] = "MagEphemFromFile -S Date -E Date InFile OutFile"; + +struct Arguments { + char *args[ nArgs ]; + + char StartDateTimeRaw[80]; + char EndDateTimeRaw[80]; + + long int StartDate; // Start date (e.g. 20130201) + long int EndDate; // End date (e.g. 20130228) + + long int StartSeconds; // Start time in seconds for the first date. + long int EndSeconds; // End time in seconds for the last date. + + char StartDateTime[80]; + char EndDateTime[80]; + + char Birds[4096]; + +}; + + +/* Parse a single option. */ +static error_t parse_opt( int key, char *arg, struct argp_state *state ) { + + double TaiSecs; + int ReturnFlag = 0; + char TimeString[40]; + Lgm_DateTime d; + Lgm_CTrans *c = Lgm_init_ctrans( 0 ); + + + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct Arguments *arguments = state->input; + switch( key ) { + case 'S': // start date + strncpy( TimeString, arg, 39 ); + strncpy( arguments->StartDateTimeRaw, arg, 39 ); arguments->StartDateTimeRaw[39] = '\0'; + IsoTimeStringToDateTime( TimeString, &d, c ); + arguments->StartDate = d.Date; + arguments->StartSeconds = d.Hour*3600 + d.Minute*60 + (int)d.Second; + Lgm_DateTimeToString( arguments->StartDateTime, &d, 0, 0 ); + break; + case 'E': // end date + strncpy( TimeString, arg, 39 ); + strncpy( arguments->EndDateTimeRaw, arg, 39 ); arguments->EndDateTimeRaw[39] = '\0'; + IsoTimeStringToDateTime( TimeString, &d, c ); + arguments->EndDate = d.Date; + + // if no explicit time field was given assume user wants whole day. + if ( strstr(TimeString, "T") == NULL ) { + // be informative about what the exact time range really will be. + arguments->EndSeconds = d.DaySeconds; + TaiSecs = d.DaySeconds + Lgm_UTC_to_TaiSeconds( &d, c ); + Lgm_TaiSeconds_to_UTC( TaiSecs, &d, c ); + } else { + arguments->EndSeconds = d.Hour*3600 + d.Minute*60 + (int)d.Second; + } + Lgm_DateTimeToString( arguments->EndDateTime, &d, 0, 0 ); + break; + case 'b': // Birds + strncpy( arguments->Birds, arg, 4095 ); + break; + + } + + return( ReturnFlag ); +} +static struct argp argp = { Options, parse_opt, ArgsDoc, doc }; + + + + +#define EXE_PATH "/home/mghenderson/git/LanlGeoMag_SAFE/Examples/MagEphem/MagEphemFromFile" + + + +/* + * Program to run MAGEIS_L3_to_L3Merged as a pool of tasks using openmp. + * The typical way to run this code is like this: + * + * ./MagEphemFromFile -F -S 20200105 -E 20200105 -b "ns55" /data2/GPS_DATA/GPS_REFORMAT/%YYYY/%YYYY%MM%DD_%B_Ephem_v1.09.txt /data2/GPS_DATA/GPS_REFORMAT/%YYYY/%YYYY%MM%DD_%B_MagEphem_v1.09.txt + * + */ +int main( int argc, char *argv[] ){ + + struct dirent **namelist; + int i, j, n, nn, nFiles, iSat, ntoken, DateFound; + struct stat StatBuf; + char Filename[256], FilenameFull[512]; + char Str[512]; + char *token, **CommandArray; + int nCommandArray; + long int Date; + + long int DatesToProcess[5000]; + int nDatesToProcess; + struct Arguments arguments; + char Box[20], OutVersion[80]; + char EphemFile[1024], ReptFile[1024], HopeFile[1024], MagEphem_File[1024], OutFile[1024]; + + int sYear, sMonth, sDay, sDoy; + int eYear, eMonth, eDay, eDoy; + int Year, Month, Day, Doy; + double sJD, eJD, JD, Hour; + + int nBirds, nBirdsTmp, iBird; + char **Birds, **BirdsTmp, Bird[80]; + + Lgm_CTrans *c = Lgm_init_ctrans( 0 ); + + + /* + * Parse CmdLine arguments and options + */ + argp_parse( &argp, argc, argv, 0, 0, &arguments ); + + Lgm_Doy( arguments.StartDate, &sYear, &sMonth, &sDay, &sDoy); + sJD = Lgm_JD( sYear, sMonth, sDay, 12.0, LGM_TIME_SYS_UTC, c ); + + Lgm_Doy( arguments.EndDate, &eYear, &eMonth, &eDay, &eDoy); + eJD = Lgm_JD( eYear, eMonth, eDay, 12.0, LGM_TIME_SYS_UTC, c ); + + + nn = 0; + for ( JD = sJD; JD <= eJD; JD += 1.0 ) { + DatesToProcess[nn] = Lgm_JD_to_Date( JD, &Year, &Month, &Day, &Hour ); + ++nn; + } + nDatesToProcess = nn; + + + LGM_ARRAY_2D( BirdsTmp, 300, 80, char ); + LGM_ARRAY_2D( Birds, 300, 80, char ); + StringSplit( arguments.Birds, BirdsTmp, 80, &nBirdsTmp ); + + + nBirds = 0; + for ( iBird = 0; iBird < nBirdsTmp; iBird++ ) { + strcpy( Bird, BirdsTmp[ iBird ] ); + strcpy( Birds[ nBirds ], BirdsTmp[ iBird ] ); + ++nBirds; + } + + + + + + nCommandArray = nDatesToProcess*2+10; + strcpy( OutVersion, "v1.0.0" ); + + /* + * Create an array of commands, so we can parcel them out with system() + */ + LGM_ARRAY_2D( CommandArray, nCommandArray, 2048, char ); + nn = 0; + for ( i=0; i= len ) StrArray[*n][len-1] = '\0'; + ++(*n); + ss = NULL; + } + +} diff --git a/Examples/Trace/3914_ctab.txt b/Examples/Trace/3914_ctab.txt new file mode 100644 index 000000000..52bb7e583 --- /dev/null +++ b/Examples/Trace/3914_ctab.txt @@ -0,0 +1,256 @@ +0 0 0 +3 0 5 +6 0 10 +10 0 15 +13 0 20 +16 0 24 +19 0 29 +21 0 33 +24 1 37 +26 1 41 +28 1 44 +30 2 47 +32 2 50 +34 2 53 +36 3 56 +37 3 58 +39 3 61 +40 4 63 +42 4 65 +43 5 68 +45 5 70 +46 6 72 +47 6 74 +48 6 75 +49 7 77 +50 7 79 +51 8 80 +52 8 82 +53 9 84 +54 9 85 +55 10 86 +56 10 88 +57 11 89 +57 11 90 +58 12 92 +59 12 93 +60 13 94 +61 14 95 +61 14 97 +62 15 98 +63 15 99 +63 16 100 +64 16 101 +65 17 102 +65 17 103 +66 18 104 +67 19 105 +67 19 106 +68 20 107 +68 20 108 +69 21 109 +70 21 110 +70 22 111 +71 23 112 +71 23 113 +72 24 114 +73 24 115 +73 25 115 +74 25 116 +74 26 117 +75 27 118 +75 27 119 +76 28 120 +76 28 121 +77 29 121 +78 30 122 +78 30 123 +79 31 124 +79 32 125 +80 32 126 +80 33 126 +81 33 127 +81 34 128 +82 35 129 +82 35 130 +83 36 130 +83 36 131 +84 37 132 +84 38 133 +85 38 133 +85 39 134 +86 40 135 +86 40 136 +87 41 137 +87 41 137 +88 42 138 +88 43 139 +89 43 140 +89 44 140 +90 45 141 +90 45 142 +91 46 143 +91 46 143 +92 47 144 +92 48 145 +93 48 146 +94 49 146 +94 50 147 +95 50 148 +95 51 149 +96 52 149 +96 52 150 +97 53 151 +97 53 152 +98 54 153 +98 55 153 +99 55 154 +99 56 155 +100 57 156 +100 57 156 +101 58 157 +101 59 158 +102 59 159 +102 60 160 +103 61 160 +103 61 161 +104 62 162 +105 63 163 +105 63 164 +106 64 164 +106 65 165 +107 65 166 +107 66 167 +108 67 168 +108 67 169 +109 68 169 +109 69 170 +110 69 171 +111 70 172 +111 71 173 +112 71 174 +112 72 174 +113 73 175 +113 74 176 +114 74 177 +115 75 178 +115 76 179 +116 76 180 +116 77 180 +117 78 181 +117 78 182 +118 79 183 +118 80 184 +119 81 185 +120 81 186 +120 82 186 +121 83 187 +121 83 188 +122 84 189 +122 85 190 +123 86 191 +123 86 192 +124 87 193 +125 88 194 +125 89 194 +126 89 195 +126 90 196 +127 91 197 +127 92 198 +128 92 199 +129 93 200 +129 94 201 +130 95 201 +130 95 202 +131 96 203 +131 97 204 +132 98 205 +133 98 206 +133 99 207 +134 100 208 +134 101 209 +135 102 209 +135 102 210 +136 103 211 +137 104 212 +137 105 213 +138 106 214 +138 107 215 +139 107 216 +140 108 216 +140 109 217 +141 110 218 +141 111 219 +142 112 220 +142 113 221 +143 113 222 +144 114 222 +144 115 223 +145 116 224 +145 117 225 +146 118 226 +147 119 227 +147 120 227 +148 121 228 +149 122 229 +149 123 230 +150 124 231 +150 125 231 +151 126 232 +152 127 233 +152 128 234 +153 129 234 +154 130 235 +154 131 236 +155 132 237 +156 133 237 +157 134 238 +157 135 239 +158 136 240 +159 137 240 +160 138 241 +160 140 242 +161 141 242 +162 142 243 +163 143 244 +164 145 244 +164 146 245 +165 147 245 +166 148 246 +167 150 246 +168 151 247 +169 152 248 +170 154 248 +171 155 249 +172 157 249 +173 158 249 +174 160 250 +175 161 250 +176 163 251 +177 164 251 +178 166 251 +179 167 252 +180 169 252 +181 170 252 +183 172 253 +184 173 253 +185 175 253 +186 177 253 +188 178 253 +189 180 254 +190 182 254 +191 183 254 +193 185 254 +194 187 254 +195 189 254 +197 190 254 +198 192 254 +200 194 254 +201 196 254 +203 198 254 +204 199 254 +205 201 254 +207 203 254 +208 205 254 +210 207 254 +212 208 254 diff --git a/Examples/Trace/5577_ctab.txt b/Examples/Trace/5577_ctab.txt new file mode 100644 index 000000000..75c55f225 --- /dev/null +++ b/Examples/Trace/5577_ctab.txt @@ -0,0 +1,256 @@ +0 0 0 +0 1 0 +0 2 1 +0 3 1 +0 5 2 +0 6 2 +1 7 2 +1 9 3 +1 10 3 +1 11 4 +1 13 4 +2 14 5 +2 15 5 +2 16 6 +2 18 6 +2 19 6 +3 20 7 +3 22 7 +3 23 7 +3 24 8 +3 25 8 +4 27 9 +4 28 9 +4 29 9 +4 30 9 +4 32 10 +4 33 10 +5 34 10 +5 35 11 +5 37 11 +5 38 11 +5 39 11 +6 40 12 +6 41 12 +6 43 12 +6 44 12 +6 45 13 +6 46 13 +7 47 13 +7 48 13 +7 50 13 +7 51 14 +7 52 14 +7 53 14 +8 54 14 +8 55 14 +8 56 14 +8 57 14 +8 58 14 +8 60 15 +9 61 15 +9 62 15 +9 63 15 +9 64 15 +9 65 15 +9 66 15 +9 67 15 +10 68 15 +10 69 15 +10 70 15 +10 71 15 +10 72 15 +10 73 15 +11 74 15 +11 75 15 +11 76 15 +11 77 15 +11 78 15 +11 79 15 +11 80 15 +11 81 15 +12 82 15 +12 83 15 +12 84 15 +12 85 14 +12 86 14 +12 87 14 +12 87 14 +13 88 14 +13 89 14 +13 90 14 +13 91 14 +13 92 13 +13 93 13 +14 94 13 +14 95 13 +14 96 14 +15 96 14 +15 97 14 +16 98 14 +16 99 14 +17 100 14 +17 101 14 +18 102 15 +18 103 15 +19 104 15 +19 104 15 +20 105 15 +20 106 15 +21 107 15 +21 108 15 +22 109 16 +22 110 16 +23 111 16 +24 112 16 +24 112 16 +25 113 16 +25 114 17 +26 115 17 +27 116 17 +27 117 17 +28 118 17 +28 119 17 +29 120 17 +30 120 18 +30 121 18 +31 122 18 +32 123 18 +33 124 18 +33 125 18 +34 126 19 +35 127 19 +36 128 19 +36 129 19 +37 130 19 +38 131 20 +39 132 20 +39 133 20 +40 134 20 +41 135 20 +42 135 21 +43 136 21 +44 137 21 +45 138 21 +45 139 21 +46 140 22 +47 141 22 +48 142 22 +49 143 22 +50 145 23 +51 146 23 +52 147 23 +53 148 23 +54 149 24 +55 150 24 +56 151 24 +58 152 25 +59 153 25 +60 154 25 +61 155 25 +62 157 26 +63 158 26 +65 159 26 +66 160 27 +67 161 27 +68 162 28 +70 164 28 +71 165 28 +72 166 29 +73 167 29 +75 168 29 +76 170 30 +78 171 30 +79 172 31 +80 173 31 +82 174 32 +83 176 32 +85 177 33 +86 178 33 +88 180 34 +89 181 34 +91 182 35 +93 183 35 +94 184 36 +96 186 37 +98 187 37 +99 188 38 +101 190 39 +103 191 39 +104 192 40 +106 193 41 +108 195 41 +110 196 42 +112 197 43 +113 198 44 +115 200 45 +117 201 45 +119 202 46 +121 203 47 +123 204 48 +125 206 49 +127 207 50 +129 208 51 +131 209 52 +133 210 53 +134 211 54 +137 213 55 +139 214 57 +141 215 58 +143 216 59 +145 217 60 +147 218 62 +149 219 63 +151 220 64 +153 221 66 +155 222 67 +157 223 69 +159 224 70 +161 225 72 +163 226 74 +165 227 75 +167 228 77 +169 228 79 +171 229 81 +174 230 83 +176 231 85 +178 232 87 +180 232 89 +182 233 91 +184 234 93 +186 235 95 +188 235 98 +190 236 100 +192 237 102 +194 237 105 +196 238 107 +198 239 110 +200 239 113 +202 240 115 +204 241 118 +206 241 121 +208 242 124 +210 242 127 +212 243 130 +214 244 133 +216 244 136 +218 245 139 +220 245 142 +221 246 145 +223 246 149 +225 247 152 +227 247 155 +228 248 159 +230 248 162 +232 249 166 +233 249 169 +235 250 173 +236 250 177 +238 251 180 +239 251 184 +241 252 188 +242 252 191 +244 253 195 +245 253 199 +246 254 203 +247 254 207 diff --git a/Examples/Trace/6300_ctab.txt b/Examples/Trace/6300_ctab.txt new file mode 100644 index 000000000..65308dc57 --- /dev/null +++ b/Examples/Trace/6300_ctab.txt @@ -0,0 +1,256 @@ +0 0 0 +1 0 0 +2 1 0 +3 1 0 +5 2 0 +6 3 0 +7 3 1 +8 4 1 +10 5 1 +11 5 1 +12 6 1 +13 6 2 +15 7 2 +16 7 2 +17 8 2 +18 9 2 +20 9 3 +21 10 3 +22 10 3 +23 11 3 +25 11 3 +26 12 3 +27 12 4 +28 13 4 +30 13 4 +31 14 4 +32 14 4 +33 15 5 +35 15 5 +36 16 5 +37 16 5 +39 17 5 +40 17 6 +41 18 6 +42 18 6 +44 18 6 +45 19 6 +46 19 6 +47 20 7 +49 20 7 +50 21 7 +51 21 7 +53 21 7 +54 22 8 +55 22 8 +56 23 8 +58 23 8 +59 23 8 +60 24 8 +62 24 9 +63 24 9 +64 25 9 +65 25 9 +67 25 9 +68 26 10 +69 26 10 +71 27 10 +72 27 10 +73 27 10 +74 27 11 +76 28 11 +77 28 11 +78 28 11 +80 29 11 +81 29 12 +82 29 12 +83 30 12 +85 30 12 +86 30 12 +87 31 12 +89 31 13 +90 31 13 +91 31 13 +93 32 13 +94 32 13 +95 32 14 +96 32 14 +98 33 14 +99 33 14 +100 33 14 +102 34 15 +103 34 15 +104 34 15 +105 34 15 +107 35 15 +108 35 15 +109 35 16 +111 35 16 +112 36 16 +113 36 16 +114 36 16 +116 36 17 +117 37 17 +118 37 17 +120 37 17 +121 37 17 +122 38 18 +123 38 18 +125 38 18 +126 38 18 +127 39 18 +128 39 19 +130 39 19 +131 39 19 +132 40 19 +134 40 19 +135 40 20 +136 40 20 +137 41 20 +138 41 20 +140 41 20 +141 42 21 +142 42 21 +143 42 21 +145 42 21 +146 43 22 +147 43 22 +148 43 22 +150 44 22 +151 44 22 +152 44 23 +153 44 23 +155 45 23 +156 45 23 +157 45 24 +158 46 24 +159 46 24 +161 46 25 +162 47 25 +163 47 25 +164 47 25 +165 48 26 +166 48 26 +168 48 26 +169 49 27 +170 49 27 +171 49 27 +172 50 27 +173 50 28 +175 50 28 +176 51 28 +177 51 29 +178 52 29 +179 52 29 +180 52 30 +181 53 30 +182 53 31 +183 53 31 +185 54 31 +186 54 32 +187 55 32 +188 55 33 +189 56 33 +190 56 34 +191 57 34 +192 57 34 +193 57 35 +194 58 35 +195 58 36 +196 59 37 +197 59 37 +198 60 38 +199 60 38 +200 61 39 +201 62 39 +202 62 40 +203 63 41 +204 63 41 +205 64 42 +206 64 43 +207 65 43 +208 66 44 +209 66 45 +210 67 46 +211 68 46 +211 68 47 +212 69 48 +213 70 49 +214 70 50 +215 71 51 +216 72 52 +217 73 53 +217 74 54 +218 74 55 +219 75 56 +220 76 57 +221 77 58 +221 78 59 +222 79 60 +223 80 61 +224 81 63 +224 82 64 +225 83 65 +226 84 66 +227 85 68 +227 86 69 +228 87 71 +229 88 72 +229 89 74 +230 91 75 +231 92 77 +231 93 78 +232 94 80 +232 96 81 +233 97 83 +234 99 85 +234 100 87 +235 102 89 +235 103 90 +236 105 92 +236 106 94 +237 108 96 +237 109 98 +238 111 100 +239 113 102 +239 115 104 +240 116 106 +240 118 109 +240 120 111 +241 122 113 +241 124 115 +242 126 117 +242 128 120 +243 130 122 +243 132 124 +244 134 127 +244 136 129 +244 138 132 +245 140 134 +245 142 137 +246 144 139 +246 147 142 +246 149 144 +247 151 147 +247 154 149 +248 156 152 +248 158 155 +248 161 157 +249 163 160 +249 165 163 +249 168 165 +250 170 168 +250 173 171 +250 175 173 +251 178 176 +251 181 179 +251 183 182 +252 186 185 +252 188 187 +252 191 190 +253 194 193 +253 197 196 +253 199 199 +254 202 202 +254 205 205 +254 208 207 diff --git a/Examples/ZeroTilt/ZeroTilt.c b/Examples/ZeroTilt/ZeroTilt.c index a2844115e..91810d72e 100644 --- a/Examples/ZeroTilt/ZeroTilt.c +++ b/Examples/ZeroTilt/ZeroTilt.c @@ -52,7 +52,7 @@ int main( ) { if ( fabs(UTc-UTa) < 1e-8 ) done = 1; } - printf("Final result:\n"); + printf("Final result for Date=%8ld :\n", Date ); printf("UTa, PSia = %.10lf %.10lf\n", UTa, Psia ); printf("UTb, PSib = %.10lf %.10lf\n", UTb, Psib ); printf("UTc, PSic = %.10lf %.10lf\n", UTc, Psic ); diff --git a/ViewDriftShell/Lgm_ParticleInfo.h b/ViewDriftShell/Lgm_ParticleInfo.h new file mode 100644 index 000000000..939d1143e --- /dev/null +++ b/ViewDriftShell/Lgm_ParticleInfo.h @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ELECTRON 0 +#define PROTON 1 + +#define GCA_ONLY 0 +#define FULL_ORBIT_ONLY 1 +#define ADAPTIVE 2 + +#define GCA 0 +#define FULL_ORBIT 1 + +typedef struct Lgm_ParticleInfo { + + Lgm_MagModelInfo *mInfo; + + + /* + * Particle properties + */ + double q; //gamma = sqrt( 1.0 + (u.x*u.x + u.y*u.y + u.z*u.z)/p->c2 ); + + + /* + * Compute uxB + */ + p->mInfo->Bfield( &P, &B, p->mInfo ); // B in nT + B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; // B in T + Lgm_CrossProduct( &u, &B, &uxB ); + + /* + * Now set up the system of equations to pass to solver + */ + g = p->q/p->m/p->gamma; + dydt[0] = u.x/p->gamma; + dydt[1] = u.y/p->gamma; + dydt[2] = u.z/p->gamma; + dydt[3] = g * ( E.x + uxB.x ); + dydt[4] = g * ( E.y + uxB.y ); + dydt[5] = g * ( E.z + uxB.z ); + + return( GSL_SUCCESS ); + +} + +/* + * 6D system for Full Orbit equations of motion. Jacobian + * The Jacobian is stored as J(i,j) = dfdy[ i*6 + j ] + */ +int J_full( double t, const double y[], double *dfdy, double dydt[], void *params ) { + + double h, g, gamma; + Lgm_Vector E, B, uxB, P, u; + Lgm_ParticleInfo *p; + p = (Lgm_ParticleInfo *)params; + + /* + * Electric field + */ + E.x = E.y = E.z = 0.0; + + + + /* + * The position vector (in Re) + */ + P.x = y[0]/1000.0/Re; P.y = y[1]/1000.0/Re; P.z = y[2]/1000.0/Re; + + /* + * The velocity vector (m/s) + */ + u.x = y[3]; u.y = y[4]; u.z = y[5]; + gamma = sqrt( 1.0 + (u.x*u.x + u.y*u.y + u.z*u.z)/p->c2 ); + + + /* + * Compute uxB + */ + p->mInfo->Bfield( &P, &B, p->mInfo ); // B in nT + B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; // B in T + Lgm_CrossProduct( &u, &B, &uxB ); + + /* + * Now set up the system of equations to pass to solver + */ + g = p->q/p->m/gamma; + dydt[0] = u.x/gamma; + dydt[1] = u.y/gamma; + dydt[2] = u.z/gamma; + dydt[3] = g * ( E.x + uxB.x ); + dydt[4] = g * ( E.y + uxB.y ); + dydt[5] = g * ( E.z + uxB.z ); + + + h = 1.0/gamma; + dfdy[ 0*6 + 0 ] = h; dfdy[ 0*6 + 1 ] = 0.0; dfdy[ 0*6 + 2 ] = 0.0; dfdy[ 0*6 + 3 ] = 0.0; dfdy[ 0*6 + 4 ] = 0.0; dfdy[ 0*6 + 5 ] = 0.0; + dfdy[ 1*6 + 0 ] = 0.0; dfdy[ 1*6 + 1 ] = h; dfdy[ 1*6 + 2 ] = 0.0; dfdy[ 1*6 + 3 ] = 0.0; dfdy[ 1*6 + 4 ] = 0.0; dfdy[ 1*6 + 5 ] = 0.0; + dfdy[ 2*6 + 0 ] = 0.0; dfdy[ 2*6 + 1 ] = 0.0; dfdy[ 2*6 + 2 ] = h; dfdy[ 2*6 + 3 ] = 0.0; dfdy[ 2*6 + 4 ] = 0.0; dfdy[ 2*6 + 5 ] = 0.0; + dfdy[ 3*6 + 0 ] = 0.0; dfdy[ 3*6 + 1 ] = 0.0; dfdy[ 3*6 + 2 ] = 0.0; dfdy[ 3*6 + 3 ] = 0.0; dfdy[ 3*6 + 4 ] = g*B.z; dfdy[ 3*6 + 5 ] = -g*B.y; + dfdy[ 4*6 + 0 ] = 0.0; dfdy[ 4*6 + 1 ] = 0.0; dfdy[ 4*6 + 2 ] = 0.0; dfdy[ 4*6 + 3 ] = -g*B.z; dfdy[ 4*6 + 4 ] = 0.0; dfdy[ 4*6 + 5 ] = g*B.x; + dfdy[ 5*6 + 0 ] = 0.0; dfdy[ 5*6 + 1 ] = 0.0; dfdy[ 5*6 + 2 ] = 0.0; dfdy[ 5*6 + 3 ] = g*B.y; dfdy[ 5*6 + 4 ] = -g*B.x; dfdy[ 5*6 + 5 ] = 0.0; + + + + + return( GSL_SUCCESS ); + +} + + + + + +/* + * 4D system for Guiding Center equations of motion. + */ +int f_gc( double t, const double y[], double dydt[], void *params ) { + + double g, h; // temporary vars + double b_dot_CurlE; + Lgm_Vector P, Q; + + Lgm_ParticleInfo *p; + p = (Lgm_ParticleInfo *)params; + + + p->p_par = y[3]; + + + /* + * Determine B, E, Grad_B, Curl_b, dbdt for given time and position. + * Lets try pure static dipole with no E-field to start. + */ + p->X.x = y[0]/1000.0/Re; p->X.y = y[1]/1000.0/Re; p->X.z = y[2]/1000.0/Re; // convert position from m -> Re + //p->mInfo->RBF_CompGradAndCurl = TRUE; + p->mInfo->Bfield( &p->X, &p->B, p->mInfo ); // B in nT + //p->mInfo->RBF_CompGradAndCurl = FALSE; + p->b = p->B; p->Bmag = Lgm_NormalizeVector( &p->b ); // b is unit vector Bmag in nT + //p->Grad_B = p->mInfo->RBF_Grad_B; + //p->Curl_b = p->mInfo->RBF_Curl_B; + + Lgm_GradB( &p->X, &p->Grad_B, LGM_DERIV_SIX_POINT, 1e-3, p->mInfo ); // Grad_B in nT/Re + g = 1e9*Re*1000.0; p->Grad_B.x /= g; p->Grad_B.y /= g; p->Grad_B.z /= g; // Grad_B now in T/m + + Lgm_Curlb( &p->X, &p->Curl_b, LGM_DERIV_SIX_POINT, 1e-3, p->mInfo ); // Curl_b in 1/Re + g = Re*1000.0; p->Curl_b.x /= g; p->Curl_b.y /= g; p->Curl_b.z /= g; // Curl_b in 1/m + + //p->Curl_E.x /= g; p->Curl_E.y /= g; p->Curl_E.z /= g; // Curl_E in V/m^2 +p->Curl_E.x = p->Curl_E.y = p->Curl_E.z = 0.0; + p->Bmag *= 1e-9; // Bmag now in T + p->B.x *= 1e-9; p->B.y *= 1e-9; p->B.z *= 1e-9; // B now in T + + /* + * Compute dbhat/dt = (-CurlE + bhat(bhat dot CurlE) ) /Bmag + */ +p->Curl_E.x = p->Curl_E.y = p->Curl_E.z = 0.0; +b_dot_CurlE = Lgm_DotProduct( &p->b, &p->Curl_E ); +Q.x = p->b.x * b_dot_CurlE; Q.y = p->b.y * b_dot_CurlE; Q.z = p->b.z * b_dot_CurlE; +Lgm_VecSub( &P, &Q, &p->Curl_E ); +p->dbdt.x = P.x/p->Bmag; p->dbdt.y = P.y/p->Bmag; p->dbdt.z = P.z/p->Bmag; // dbdt in s^-1 +//printf("dbdt = %g %g %g\n", p->dbdt.x, p->dbdt.y, p->dbdt.z); + + //p->E.x = p->mInfo->RBF_E.x; // Ex in V/m + //p->E.y = p->mInfo->RBF_E.y; // Ey in V/m + //p->E.z = p->mInfo->RBF_E.z; // Ez in V/m +p->E.x = 0.0; p->E.y = 0.0; p->E.z = 0.0; // E in V/m + + + /* + * Compute The relativistic factor gamma and its gradient. See Eq (6) of + * Tao et al. [2007] for gamma. Its gradient is (see text immediately after + * Eq (17) of Tao et al. [2007]); + * Grad gamma = (mu*Grad B)/(gamma m c^2) + */ + g = p->p_par/p->mc; + p->gamma = sqrt( 1.0 + 2.0*p->mu*p->Bmag/p->mc2 + g*g ); + +//double hh = 2.0*p->mu*p->Bmag/p->mc2 + g*g; +//double KineticEnergy_n = p->m*hh*p->c2/(p->gamma + 1.0)*6.241509e+15; +//printf("mu = %g KineticEnergy_n=%g\n", p->mu, KineticEnergy_n); + g = p->mu/(p->gamma*p->mc2); + p->Grad_gamma.x = g*p->Grad_B.x; + p->Grad_gamma.y = g*p->Grad_B.y; + p->Grad_gamma.z = g*p->Grad_B.z; +//printf("Grad_gamma = %g %g %g\n", p->Grad_gamma.x, p->Grad_gamma.y, p->Grad_gamma.z); + + /* + * Compute "Effective B Field", B*. Eq (15) of Tao et al. [2007]. (Bs = + * Bstar). + */ + //g = p->c*p->p_par/p->q; + g = p->p_par/p->q; + p->Bs.x = p->B.x + g*p->Curl_b.x; + p->Bs.y = p->B.y + g*p->Curl_b.y; + p->Bs.z = p->B.z + g*p->Curl_b.z; +//printf("B = %g %g %g\n", p->B.x, p->B.y, p->B.z); + + + /* + * Compute "Effective E Field", E*. Eq (16) of Tao et al. [2007]. (Es = + * Estar). + * Note that in Cary and Brizard, the formula for E* = E - 1/q (mc^2Grad_Gamma - p_par dbdt) (see Eqn 6.27) + * I.e. the sign of dbdt term is opposite. + */ + g = p->p_par/p->q; + h = p->mc2/p->q; + //p->Es.x = p->E.x - g*p->dbdt.x - h*p->Grad_gamma.x; + //p->Es.y = p->E.y - g*p->dbdt.y - h*p->Grad_gamma.y; + //p->Es.z = p->E.z - g*p->dbdt.z - h*p->Grad_gamma.z; + p->Es.x = p->E.x + g*p->dbdt.x - h*p->Grad_gamma.x; //Cary and Brizard, [2009] + p->Es.y = p->E.y + g*p->dbdt.y - h*p->Grad_gamma.y; //Cary and Brizard, [2009] + p->Es.z = p->E.z + g*p->dbdt.z - h*p->Grad_gamma.z; //Cary and Brizard, [2009] + +//printf("CurlE = %g %g %g g*p->dbdt = %g %g %g h*p->Grad_gamma = %g %g %g\n", p->mInfo->RBF_Curl_E.x, p->mInfo->RBF_Curl_E.y, p->mInfo->RBF_Curl_E.z, g*p->dbdt.x, g*p->dbdt.y, g*p->dbdt.z, h*p->Grad_gamma.x, h*p->Grad_gamma.y, h*p->Grad_gamma.z ); + + /* + * Compute B*_par. Eq (17) of Tao et al. [2007]. + */ + p->Bs_par = Lgm_DotProduct( &p->Bs, &p->b ); + + + /* + * Compute E* x b (needed in Eqn (13) of Tao et al. [2007]. + */ + Lgm_CrossProduct( &p->Es, &p->b, &p->Es_cross_b ); + + + + /**************************************************************************** + * Relativistic Guiding Center equations. Eqs (13-14) of Tao et al. * + * [2007]. Xdot = velocity of guiding center, v_par_dot is rate of change * + * of parallel velocity. * + * * + ****************************************************************************/ + g = p->p_par/(p->gamma*p->m); + //h = p->c; + h = 1.0; // this makes the formula consistent with the MKSA system + p->X_dot.x = ( g*p->Bs.x + h*p->Es_cross_b.x ) / p->Bs_par; + p->X_dot.y = ( g*p->Bs.y + h*p->Es_cross_b.y ) / p->Bs_par; + p->X_dot.z = ( g*p->Bs.z + h*p->Es_cross_b.z ) / p->Bs_par; + + p->p_par_dot = p->q * Lgm_DotProduct( &p->Es, &p->Bs ) / p->Bs_par; + //p->v_par_dot = p->q * Lgm_DotProduct( &p->Es, &p->Bs ) / p->Bs_par / p->m; + + + /* + * Now set up the system of equations to pass to solver + */ + dydt[0] = p->X_dot.x; + dydt[1] = p->X_dot.y; + dydt[2] = p->X_dot.z; + dydt[3] = p->p_par_dot; +//printf("Derivs: %g %g %g %g\n", dydt[0], dydt[1], dydt[2], dydt[3] ); + + return( GSL_SUCCESS ); + +} + + +int Lgm_TraceParticle_GCA( Lgm_Vector *ParticlePosition, long int *nParticlePosition ) { + + Lgm_ParticleInfo p; + double v_par, v_per, v_tot, p_par; + long int Date; + double UTC; + double Ra, MLT, Phi, Lat; + Lgm_Vector u_sm, u0; + int Flag; + Lgm_Vector v1, v2, v3, Bvec, un; + double AlphaEq, g, Blocal, Beq, Tb_approx, gamma, gamma_n, KineticEnergy_n; + + + // Setup particle properties + p.KE = 10000.0/1000.0; // Initial Kinetic Energy MeV + AlphaEq = 85.0; g = sin( AlphaEq*RadPerDeg ); + p.q = 1.60217657e-19; // Particle charge, C + p.m = 9.10938291e-31; // electron mass, kg + //p.m = 1.67262178e-27; // proton mass, kg + p.c = 2.99792458e8; // Speed of light, m/s + p.c2 = p.c * p.c; // c^2 + p.mc = p.m * p.c; + p.mc2 = p.m * p.c * p.c; + + + + // Date -- really should be taken from elsewhere.. + Date = 20020418; + UTC = 5.0 + 30.0/60.0 + 0.0/3600.0; + + p.mInfo = Lgm_InitMagInfo( ); + Lgm_Set_Coord_Transforms( Date, UTC, p.mInfo->c ); + p.mInfo->Kp = 2; + Lgm_MagModelInfo_Set_MagModel( LGM_IGRF, LGM_EXTMODEL_T89c, p.mInfo ); +// Lgm_MagModelInfo_Set_MagModel( LGM_CDIP, LGM_EXTMODEL_NULL, p.mInfo ); + + // Set initial position - make user selectable + Ra = 6.60000; + MLT = -1.5; + Phi = (MLT*15.0 - 180.0)*RadPerDeg; Lat = 0.0*RadPerDeg; + u_sm.x = Ra*cos( Phi )*cos(Lat); u_sm.y = Ra*sin( Phi )*cos(Lat); u_sm.z = Ra*sin(Lat); + Lgm_Convert_Coords( &u_sm, &u0, SM_TO_GSM, p.mInfo->c ); + + + + Flag = Lgm_Trace( &u0, &v1, &v2, &v3, 120.0, 1e-7, 1e-7, p.mInfo ); + p.mInfo->Bfield( &u0, &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + p.mInfo->Bfield( &v3, &Bvec, p.mInfo ); Beq = Lgm_Magnitude( &Bvec ); + //printf("Blocal, Beq = %g %g\n", Blocal, Beq); + + + p.B0 = Blocal; // Initial |B| in nT. + p.Alpha = asin( sqrt( Blocal/Beq * g*g ) )*DegPerRad; +//v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ep0 ) ) * p.c; +v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ee0 ) ) * p.c; + v_par = v_tot * cos( p.Alpha*RadPerDeg ); + v_per = v_tot * sin( p.Alpha*RadPerDeg ); + gamma = 1.0/sqrt( 1.0 - v_tot*v_tot/p.c2 ); + +g = gamma*v_tot; +gamma_n = sqrt( 1.0 + g*g/p.c2 ); +KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; +printf("KineticEnergy_n=%g\n", KineticEnergy_n); + +//p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ep0 ); // mu in MeV/G +p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ee0 ); // mu in MeV/G +//printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ep0 = %g MeV Mu = %g MeV/G\n", p.KE, p.Alpha, p.B0, LGM_Ep0, p.mu ); +printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ee0 = %g MeV Mu = %g MeV/G\n", p.KE, p.Alpha, p.B0, LGM_Ee0, p.mu ); + p.mu *= 1.60217657e-9; // mu in J/T + + + p_par = gamma*p.m*v_par; + + + + int nSteps; + double y[4]; + nSteps = 50000; + *nParticlePosition = 0; + ParticlePosition[0] = u0; ++(*nParticlePosition); + y[0] = u0.x*1000.0*Re; + y[1] = u0.y*1000.0*Re; + y[2] = u0.z*1000.0*Re; + y[3] = p_par; + + + + // Setup ODE solver in GSL + void *params; + params = (void *)&p; + const gsl_odeiv2_step_type *T = gsl_odeiv2_step_rk8pd; + //const gsl_odeiv2_step_type *T = gsl_odeiv2_step_rk4; + + gsl_odeiv2_step *s = gsl_odeiv2_step_alloc( T, 4 ); + gsl_odeiv2_control *c = gsl_odeiv2_control_standard_new( 1e-6, 1e-6, 1.0, 1.0 ); + gsl_odeiv2_evolve *e = gsl_odeiv2_evolve_alloc( 4 ); + + gsl_odeiv2_system sys = { f_gc, NULL, 4, params }; + +FILE *fp; +fp = fopen( "TestParticle.txt", "w"); + + + + //See Hess or Anderson, 1966 (Annual Review of Nuc. Sci.) +//Tb_approx = 0.085* Ra/sqrt( Lgm_v2overc2( p.KE, LGM_Ep0 ) ) * (1.3 - 0.56*sin(p.Alpha*RadPerDeg) ); +Tb_approx = 0.085* Ra/sqrt( Lgm_v2overc2( p.KE, LGM_Ee0 ) ) * (1.3 - 0.56*sin(p.Alpha*RadPerDeg) ); + printf("Approximate Bounce Period in Dipole: %g seconds\n", Tb_approx ); + + + g = gamma*v_tot; + gamma_n = sqrt( 1.0 + g*g/p.c2 ); + KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; + p.mInfo->Bfield( &ParticlePosition[0], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + fprintf( fp, "%g %g %g %g %g %g\n", 0.0, ParticlePosition[0].x, ParticlePosition[0].y, ParticlePosition[0].z, Blocal, KineticEnergy_n ); +printf( "START GCA: %g %g %g %g %g %g\n", 0.0, ParticlePosition[0].x, ParticlePosition[0].y, ParticlePosition[0].z, Blocal, KineticEnergy_n ); + + + double h = 1e-6, t, ti, dT; + long int i; + int status; + t = 0.0; + dT = 0.01; +dT = 0.001; +// ti = 3600.0; + for ( i = 1; i <= nSteps; i++ ) { + + ti = i*dT; + while ( t < ti ) { + + if ( h > (ti-t) ) h = ti-t; + + status = gsl_odeiv2_evolve_apply( e, c, s, &sys, &t, ti, &h, y ); + +//printf( "h = %g ti = %g %g %g %g %g %g\n", h, ti, t, y[0]/1000.0/Re, y[1]/1000.0/Re, y[2]/1000.0/Re, y[3] ); + + if ( status != GSL_SUCCESS ) { + h = 1e-6; + printf ("error, return value=%d\n", status); + //exit(0); + break; + } else { + dT = 0.001; + } + } + + g = gamma*v_tot; + gamma_n = sqrt( 1.0 + g*g/p.c2 ); + KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; + + double xxx, yyy, zzz; + xxx = y[0]/1000.0/Re; + yyy = y[1]/1000.0/Re; + zzz = y[2]/1000.0/Re; + + if ( ( xxx > 15.0) || ( xxx < -200.0) || ( fabs( yyy ) > 30.0 ) || ( fabs( zzz ) > 30.0 ) ) { + printf("Particle lost: ParticlePosition[%d] = %g %g %g\n", i, ParticlePosition[i].x, ParticlePosition[i].y, ParticlePosition[i].z ); + break; + } + + if ((i%1 == 0)&&( *nParticlePosition<50000)){ + ParticlePosition[*nParticlePosition].x = xxx; + ParticlePosition[*nParticlePosition].y = yyy; + ParticlePosition[*nParticlePosition].z = zzz; + + p.mInfo->Bfield( &ParticlePosition[*nParticlePosition], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + fprintf( fp, "%g %g %g %g %g %g\n", t, xxx, yyy, zzz, Blocal, KineticEnergy_n ); + fflush( fp ); + + ++(*nParticlePosition); + } + + + } + + //gsl_odeiv2_driver_free( d ); + gsl_odeiv2_evolve_free( e ); + gsl_odeiv2_control_free( c ); + gsl_odeiv2_step_free( s ); + +fclose(fp); + + +} + + + +/* + * Implements the relativistic "Boris push" to update the velocity. + * Inputs: dt, unm1o2, E, B, p + * Outputs: unp1o2, gamma_np1o2, gamma_n + * Note that gamma_n can be used to compute the "tiome centered kinetic energy". (See Birdsall and Langdon page 357.) + */ +int UpdateVelocityBoris( Lgm_Vector *unm1o2, Lgm_Vector *unp1o2, double *gamma_np1o2, double *gamma_n, Lgm_Vector *E, Lgm_Vector *B, double dt, Lgm_ParticleInfo *p ) { + + Lgm_Vector um, up, uu, w; + Lgm_Vector s, t, e, bhat; + double g, Bmag; + + /* + * Compute the "half acceleration" + */ + g = 0.5*dt*p->q/p->m; + e.x = g*E->x; e.y = g*E->y; e.z = g*E->z; + + + /* + * Compute u- by adding the half-accel, e to u_n-1/2 + */ + um.x = unm1o2->x + e.x; um.y = unm1o2->y + e.y; um.z = unm1o2->z + e.z; + + + /* + * Compute gamma_n = sqrt( 1 + (um/c)^2 ) + */ + g = um.x*um.x + um.y*um.y + um.z*um.z; // |um|^2 + *gamma_n = sqrt( 1.0 + g/p->c2 ); + + + /* + * Compute the vectors needed to implement the rotation from v- to v+ + * The angle of rotation is theta where tan(theta/2) where theta = q dt B/(m gamma-) + */ + // normal approximation + g = p->q*dt/(2.0* (*gamma_n) * p->m); + t.x = g*B->x; t.y = g*B->y; t.z = g*B->z; + + // This is for Boris push with gyrophase correction +// bhat = *B; Bmag = Lgm_NormalizeVector( &bhat ); +// g = tan(p->q*dt*Bmag/(2.0*(*gamma_n) * p->m)); +// t.x = g*bhat.x; t.y = g*bhat.y; t.z = g*bhat.z; + + + g = 2.0/(1.0 + t.x*t.x + t.y*t.y + t.z*t.z); + s.x = g*t.x; s.y = g*t.y; s.z = g*t.z; + + + /* + * Do Boris rotation + */ + Lgm_CrossProduct( &um, &t, &w ); // w = um x t + uu.x = um.x + w.x; uu.y = um.y + w.y; uu.z = um.z + w.z; // u^prime = u- + u- x t + + Lgm_CrossProduct( &uu, &s, &w ); // w = u^prime x s + up.x = um.x + w.x; up.y = um.y + w.y; up.z = um.z + w.z; // u+ = u- + u^prime x s + + + /* + * Add final half acceleration to get u_n+1/2 + */ + unp1o2->x = up.x + e.x; unp1o2->y = up.y + e.y; unp1o2->z = up.z + e.z; + + + /* + * Finally compute and save the gamma_n+1/2 value (needed for position advancing) + */ + g = Lgm_Magnitude( unp1o2 )/p->c; + *gamma_np1o2 = sqrt( 1.0 + g*g ); + //printf("unp1o2 = %g %g %g, p->c = %g g, gamma_np1o2 = %g %g\n", unp1o2->x, unp1o2->y, unp1o2->z, p->c, g, *gamma_np1o2); + + +} + + + +/* + * Simple euler scheme to update velocity. + * Needed to get Boris leapfrog scheme going.. + */ +int UpdateVelocityEuler( Lgm_Vector *u0, Lgm_Vector *u1, Lgm_Vector *E, Lgm_Vector *B, double dt, Lgm_ParticleInfo *p ) { + + + Lgm_Vector G, u_cross_B; + double g, gamma0; + + /* + * Compute u x B + */ + Lgm_CrossProduct( u0, B, &u_cross_B ); + + /* + * Compute F/m + */ + g = Lgm_Magnitude( u0 ); + gamma0 = sqrt( 1.0 + g*g/p->c2 ); + g = p->q/p->m; + G.x = g * (E->x + u_cross_B.x/gamma0); G.y = g * (E->y + u_cross_B.y/gamma0); G.z = g * (E->z + u_cross_B.z/gamma0); + + /* + * Update u0 -> u1 + */ + u1->x = u0->x + dt * G.x; u1->y = u0->y + dt*G.y; u1->z = u0->z + dt*G.z; + +} + +int UpdatePositionBoris( Lgm_Vector *wn, Lgm_Vector *wnp1, Lgm_Vector *unp1o2, double gamma_np1o2, double dt, Lgm_ParticleInfo *p ) { + + wnp1->x = wn->x + unp1o2->x * dt / gamma_np1o2; + wnp1->y = wn->y + unp1o2->y * dt / gamma_np1o2; + wnp1->z = wn->z + unp1o2->z * dt / gamma_np1o2; +//printf("gamma_np1o2 = %g\n", gamma_np1o2); + +} + + + +int Lgm_TraceParticle_Boris( Lgm_Vector *ParticlePosition, long int *nParticlePosition ) { + + Lgm_ParticleInfo p; + double v_tot, v_par, v_per; + long int Date; + double UTC; + double Ra, MLT, Phi, Lat; + Lgm_Vector Rc_hat_pqb, Rc_hat_gsm, v_per_hat_pqb, v_per_hat_gsm; + Lgm_Vector w_sm, w0, w_off, ww; + int Flag; + Lgm_Vector u0, u1; + Lgm_Vector v1, v2, v3, Bvec, bhat, B, E; + double AlphaEq, g, Blocal, Beq, Tb_approx, t, dt, Rc, phi; + + Lgm_Vector unm1o2, unp1o2, wn, wnp1, un; + double gamma_np1o2, gamma, gamma_n, gamma_n2, KineticEnergy_n; + int i; + + + // Setup particle properties + p.KE = 5000.0/1000.0; // Initial Kinetic Energy MeV + AlphaEq = 85.0; g = sin( AlphaEq*RadPerDeg ); + p.q = 1.60217657e-19; // Particle charge, C + //p.m = 9.10938291e-31; // electron mass, kg + p.m = 1.67262178e-27; // proton mass, kg + p.c = 2.99792458e8; // Speed of light, m/s + p.c2 = p.c * p.c; // c^2 + p.mc = p.m * p.c; + p.mc2 = p.m * p.c * p.c; + + + + // Date -- really should be taken from elsewhere.. + Date = 20020418; + UTC = 5.0 + 30.0/60.0 + 0.0/3600.0; + + p.mInfo = Lgm_InitMagInfo( ); + Lgm_Set_Coord_Transforms( Date, UTC, p.mInfo->c ); + p.mInfo->Kp = 2; + Lgm_MagModelInfo_Set_MagModel( LGM_IGRF, LGM_EXTMODEL_T89c, p.mInfo ); +// Lgm_MagModelInfo_Set_MagModel( LGM_CDIP, LGM_EXTMODEL_NULL, p.mInfo ); + + // Set initial position - make user selectable + Ra = 6.60000; + MLT = -1.5; + Phi = (MLT*15.0 - 180.0)*RadPerDeg; Lat = 0.0*RadPerDeg; + w_sm.x = Ra*cos( Phi )*cos(Lat); w_sm.y = Ra*sin( Phi )*cos(Lat); w_sm.z = Ra*sin(Lat); + Lgm_Convert_Coords( &w_sm, &w0, SM_TO_GSM, p.mInfo->c ); + + + + Flag = Lgm_Trace( &w0, &v1, &v2, &v3, 120.0, 1e-7, 1e-7, p.mInfo ); + p.mInfo->Bfield( &v3, &Bvec, p.mInfo ); Beq = Lgm_Magnitude( &Bvec ); + p.mInfo->Bfield( &w0, &Bvec, p.mInfo ); B = Bvec; bhat = Bvec; Blocal = Lgm_NormalizeVector( &bhat ); + //printf("Blocal, Beq = %g %g\n", Blocal, Beq); + B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; + + + p.B0 = Blocal; // Initial |B| in nT. + p.Alpha = asin( sqrt( Blocal/Beq * g*g ) )*DegPerRad; + g = sin( p.Alpha * RadPerDeg ); + double B_mirror = Blocal/(g*g); +//v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ep0 ) ) * p.c; +v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ee0 ) ) * p.c; + v_par = v_tot * cos( p.Alpha*RadPerDeg ); + v_per = v_tot * sin( p.Alpha*RadPerDeg ); + +//p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ep0 ); // mu in MeV/G +p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ee0 ); // mu in MeV/G +//printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ep0 = %g MeV Mu = %g MeV/G B_mirror = %g\n", p.KE, p.Alpha, p.B0, LGM_Ep0, p.mu, B_mirror ); +printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ee0 = %g MeV Mu = %g MeV/G B_mirror = %g\n", p.KE, p.Alpha, p.B0, LGM_Ee0, p.mu, B_mirror ); + p.mu *= 1.60217657e-9; // mu in J/T + + + + FILE *fp; + fp = fopen( "TestParticle2.txt", "w"); //Boris + + + double Tc; + //See Hess or Anderson, 1966 (Annual Review of Nuc. Sci.) + Tc = 2.0*M_PI * p.m/fabs(p.q * p.B0*1e-9); + printf("Approximate Cycoltron Period in Dipole: %g seconds\n", Tc ); + + //printf("w0 = %g %g %g\n", w0.x, w0.y, w0.z); + + + + + + +// FROM HERE +// We are shifting from GCA to Full orbit positions +// Initial GCA position is w0 + /* + * Our original pointm is the GC point. Here, we shift to full orbit position. + */ + Lgm_Set_GSM_TO_PQB( &w0, p.mInfo ); // This will set up the PQB coord system for this location + + /* + * Need to pick a phase angle in the perp plane to get where to offset particle + */ + phi = 90.0; Rc_hat_pqb.x = cos(phi*RadPerDeg); Rc_hat_pqb.y = sin(phi*RadPerDeg); Rc_hat_pqb.z = 0.0; + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &Rc_hat_pqb, &Rc_hat_gsm ); // dont need to shift system for velocity + //printf("Rc_hat_pqb = %g %g %g Rc_hat_gsm = %g %g %g\n", Rc_hat_pqb.x, Rc_hat_pqb.y, Rc_hat_pqb.z, Rc_hat_gsm.x, Rc_hat_gsm.y, Rc_hat_gsm.z); + + + v_per_hat_pqb.x = -sin(phi*RadPerDeg); v_per_hat_pqb.y = cos(phi*RadPerDeg); v_per_hat_pqb.z = 0.0;; + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); // dont need to shift system for velocity + //printf("v_per_hat_pqb = %g %g %g v_per_hat_gsm = %g %g %g\n", v_per_hat_pqb.x, v_per_hat_pqb.y, v_per_hat_pqb.z, v_per_hat_gsm.x, v_per_hat_gsm.y, v_per_hat_gsm.z); + + + /* + * We need to offset the starting position of the particle based on its gyroradius. + */ + gamma = 1.0/sqrt( 1.0 - v_tot*v_tot/p.c2); + Rc = fabs(gamma*p.m*v_per/(p.q*Blocal*1e-9)); // particle gyro-radius in meters + //printf("Blocal = %g Rc = %g\n", Blocal, Rc); + + + w_off.x = w0.x*1000.0*Re + Rc * Rc_hat_gsm.x; // m + w_off.y = w0.y*1000.0*Re + Rc * Rc_hat_gsm.y; // m + w_off.z = w0.z*1000.0*Re + Rc * Rc_hat_gsm.z; // m + //printf("w_off = %g %g %g\n", w_off.x/1000.0/Re, w_off.y/1000.0/Re, w_off.z/1000.0/Re); + + + /* + * Recompute local b-field related quantities. + * + * We are shifting where the particle is (from the GC to its full orbit helix). + * A problem with this is that the b-hast is slightly different at this ne location. + * If we just use the previous b-hat and v-perp directions, thinks will be a bit off. + * (You have to be careful here or KE wont be conserved.) + */ + ww.x = w_off.x/1000.0/Re; ww.y = w_off.y/1000.0/Re; ww.z = w_off.z/1000.0/Re; + + // New (slightly different) b-hat. + p.mInfo->Bfield( &ww, &bhat, p.mInfo ); Blocal = Lgm_NormalizeVector( &bhat ); + + // New (slightly different) v_per_hat_pqb and v_per_hat_gsm vector. + Lgm_Set_GSM_TO_PQB( &ww, p.mInfo ); + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); + + // Recompute local pitch angle (because |Blocal| is slightly different). + p.Alpha = asin( sqrt( Blocal/B_mirror ) )*DegPerRad; + printf("Blocal, p.Alpha = %g, %g\n", Blocal, p.Alpha); + + // Reset u0 = w_off + w0 = w_off; +// TO HERE +// We are shifting from GCA to Full orbit positions + + + + + + + + + + + + + + + + + // partition of v_tot between v_par and v_per. + v_par = v_tot * cos( p.Alpha*RadPerDeg ); + v_per = v_tot * sin( p.Alpha*RadPerDeg ); + + + // initial velocity vector u = gamma*v + //printf("v_par, v_per = %g %g Bvec = %g %g %g v_per_hat_gsm = %g %g %g\n", v_par, v_per, Bvec.x, Bvec.y, Bvec.z, v_per_hat_gsm.x, v_per_hat_gsm.y, v_per_hat_gsm.z); + u0.x = gamma*(v_par * bhat.x + v_per * v_per_hat_gsm.x); + u0.y = gamma*(v_par * bhat.y + v_per * v_per_hat_gsm.y); + u0.z = gamma*(v_par * bhat.z + v_per * v_per_hat_gsm.z); + //printf("bhat dot v_per_hat_gsm = %g Lgm_Magnitude( &v_per_hat_gsm ) = %g\n", Lgm_DotProduct( &bhat, &v_per_hat_gsm ), Lgm_Magnitude( &v_per_hat_gsm ) ); + + +Lgm_Vector vvhat; +vvhat = u0; +Lgm_NormalizeVector( &vvhat ); +double NewPA; +NewPA = acos( Lgm_DotProduct( &bhat, &vvhat ) ) * DegPerRad; + + +printf("Lgm_Magnitude( &bhat ) = %g\n", Lgm_Magnitude( &bhat )); +printf("Lgm_Magnitude( &vvhat ) = %g\n", Lgm_Magnitude( &vvhat )); +printf("bhat = %g %g %g\n", bhat.x, bhat.y, bhat.z); +printf("vvhat = %g %g %g\n", vvhat.x, vvhat.y, vvhat.z); +printf("Pitch ANGLE: %g\n", NewPA); +//exit(0); + + /* + * Backup the velocity by 1/2 time step. And then do Boris stepping + */ + wn = w0; + E.x = E.y = E.z = 0.0; + t = 0.0; dt = 0.00001; + UpdateVelocityEuler( &u0, &unm1o2, &E, &B, -dt/2.0, &p ); +//printf("u0 = %g %g %g\n", u0.x, u0.y, u0.z); +//printf("unm1o2 = %g %g %g\n", unm1o2.x, unm1o2.y, unm1o2.z); + *nParticlePosition = 0; + ParticlePosition[0].x = wn.x/1000.0/Re; + ParticlePosition[0].y = wn.y/1000.0/Re; + ParticlePosition[0].z = wn.z/1000.0/Re; + ++(*nParticlePosition); + +g = Lgm_Magnitude( &u0 ); +gamma_n2 = 1.0 + g*g/p.c2; +gamma_n = sqrt( gamma_n2 ); +KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; +printf("|u0| = %.12lf\n", g); +printf("|u0|^2 = %.12lf\n", g*g); +printf("c^2 = %.12lf\n", p.c2); +printf("m = %g\n", p.m); +printf("gamma_n = %.12lf\n", gamma_n); +printf("KineticEnergy_n = %.12lf\n", KineticEnergy_n); +p.mInfo->Bfield( &ParticlePosition[0], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); +fprintf( fp, "%g %g %g %g %g %g\n", 0.0, ParticlePosition[0].x, ParticlePosition[0].y, ParticlePosition[0].z, Blocal, KineticEnergy_n ); fflush( fp ); +printf( "START Boris: %g %g %g %g %g %g\n", 0.0, ParticlePosition[0].x, ParticlePosition[0].y, ParticlePosition[0].z, Blocal, KineticEnergy_n ); + + + for (i=1; i<1320000; i++){ + + + ww.x = wn.x/1000.0/Re; ww.y = wn.y/1000.0/Re; ww.z = wn.z/1000.0/Re; // Re + p.mInfo->Bfield( &ww, &B, p.mInfo ); // B in nT + B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; // B in T + + // Update veolicty + UpdateVelocityBoris( &unm1o2, &unp1o2, &gamma_np1o2, &gamma_n, &E, &B, dt, &p ); + + // update position +//printf("gamma_n, gamma_np1o2 = %g %g\n", gamma_n, gamma_np1o2); + UpdatePositionBoris( &wn, &wnp1, &unp1o2, gamma_np1o2, dt, &p ); + t += dt; + + KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; + + if ((i%1 == 0)&&( *nParticlePosition<50000)){ + ParticlePosition[*nParticlePosition].x = wnp1.x/1000.0/Re; + ParticlePosition[*nParticlePosition].y = wnp1.y/1000.0/Re; + ParticlePosition[*nParticlePosition].z = wnp1.z/1000.0/Re; + + p.mInfo->Bfield( &ParticlePosition[*nParticlePosition], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + fprintf( fp, "%g %g %g %g %g %g\n", t, ParticlePosition[*nParticlePosition].x, ParticlePosition[*nParticlePosition].y, ParticlePosition[*nParticlePosition].z, Blocal, KineticEnergy_n ); + fflush( fp ); + + + + ++(*nParticlePosition); + } + + unm1o2 = unp1o2; // set previous vel. to current vel. + wn = wnp1; // set previous pos. to current pos. + + } + +} + + + + +int Lgm_TraceParticle_Full( Lgm_Vector *ParticlePosition, long int *nParticlePosition, +Lgm_Vector *ParticlePosition2, long int *nParticlePosition2 ) { + + Lgm_ParticleInfo p; + double v_tot, v_par, v_per; + long int Date; + double UTC; + double Ra, MLT, Phi, Lat; + Lgm_Vector u_sm, u0; + int Flag; + Lgm_Vector v1, v2, v3, Bvec, bhat, B, E; + Lgm_Vector Rc_hat_pqb, Rc_hat_gsm, v_per_hat_pqb, v_per_hat_gsm; + Lgm_Vector w_sm, w0, w_off, ww, un; + double AlphaEq, g, Blocal, Beq, Tb_approx, t, dt, Rc, phi, gamma; + + double gamma_n, KineticEnergy_n; + + + // Setup particle properties + p.KE = 5000.0/1000.0; // Initial Kinetic Energy MeV + AlphaEq = 85.0; g = sin( AlphaEq*RadPerDeg ); + p.q = 1.60217657e-19; // Particle charge, C + //p.m = 9.10938291e-31; // electron mass, kg + p.m = 1.67262178e-27; // proton mass, kg + p.c = 2.99792458e8; // Speed of light, m/s + p.c2 = p.c * p.c; // c^2 + p.mc = p.m * p.c; + p.mc2 = p.m * p.c * p.c; + + + + // Date -- really should be taken from elsewhere.. + Date = 20020418; + UTC = 5.0 + 30.0/60.0 + 0.0/3600.0; + + p.mInfo = Lgm_InitMagInfo( ); + Lgm_Set_Coord_Transforms( Date, UTC, p.mInfo->c ); + p.mInfo->Kp = 2; + Lgm_MagModelInfo_Set_MagModel( LGM_IGRF, LGM_EXTMODEL_T89c, p.mInfo ); +// Lgm_MagModelInfo_Set_MagModel( LGM_CDIP, LGM_EXTMODEL_NULL, p.mInfo ); + + // Set initial position - make user selectable + Ra = 6.60000; + MLT = -1.5; + Phi = (MLT*15.0 - 180.0)*RadPerDeg; Lat = 0.0*RadPerDeg; + u_sm.x = Ra*cos( Phi )*cos(Lat); u_sm.y = Ra*sin( Phi )*cos(Lat); u_sm.z = Ra*sin(Lat); + Lgm_Convert_Coords( &u_sm, &w0, SM_TO_GSM, p.mInfo->c ); + + + + Flag = Lgm_Trace( &w0, &v1, &v2, &v3, 120.0, 1e-7, 1e-7, p.mInfo ); + p.mInfo->Bfield( &v3, &Bvec, p.mInfo ); Beq = Lgm_Magnitude( &Bvec ); + p.mInfo->Bfield( &w0, &Bvec, p.mInfo ); B = Bvec; bhat = Bvec; Blocal = Lgm_NormalizeVector( &bhat ); + //printf("Blocal, Beq = %g %g\n", Blocal, Beq); + B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; + + + p.B0 = Blocal; // Initial |B| in nT. + p.Alpha = asin( sqrt( Blocal/Beq * g*g ) )*DegPerRad; + g = sin( p.Alpha * RadPerDeg ); + double B_mirror = Blocal/(g*g); +//v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ep0 ) ) * p.c; +v_tot = sqrt( Lgm_v2overc2( p.KE, LGM_Ee0 ) ) * p.c; +v_par = v_tot * cos( p.Alpha*RadPerDeg ); +v_per = v_tot * sin( p.Alpha*RadPerDeg ); + +//p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ep0 ); // mu in MeV/G +p.mu = Lgm_Ek_to_Mu( p.KE, p.Alpha, p.B0, LGM_Ee0 ); // mu in MeV/G +//printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ep0 = %g MeV Mu = %g MeV/G B_mirror = %g\n", p.KE, p.Alpha, p.B0, LGM_Ep0, p.mu, B_mirror ); +printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ee0 = %g MeV Mu = %g MeV/G B_mirror = %g\n", p.KE, p.Alpha, p.B0, LGM_Ee0, p.mu, B_mirror ); + p.mu *= 1.60217657e-9; // mu in J/T + + +gamma_n = 1.0/sqrt(1.0-v_tot*v_tot/p.c2); +KineticEnergy_n = (gamma_n - 1.0)*p.m*p.c2*6.241509e+15; +printf("gamma_n, KE = %lf, %lf\n", gamma_n, KineticEnergy_n); + +double uuu; +uuu = v_tot*gamma_n; +KineticEnergy_n = p.m*uuu*uuu/(1.0+gamma_n)*6.241509e+15; +printf("uuu = %g gamma_n = %g KE = %lf\n", uuu, gamma_n, KineticEnergy_n); + + + +//exit(0); + + + + + double Tc; + //printf("w0 = %g %g %g\n", w0.x, w0.y, w0.z); + Lgm_Set_GSM_TO_PQB( &w0, p.mInfo ); // This will set up the PQB coord system for this location + + /* + * Need to pick a phase angle in the perp plane to get where to offset particle + */ + phi = 90.0; Rc_hat_pqb.x = cos(phi*RadPerDeg); Rc_hat_pqb.y = sin(phi*RadPerDeg); Rc_hat_pqb.z = 0.0; + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &Rc_hat_pqb, &Rc_hat_gsm ); // dont need to shift system for velocity + //printf("Rc_hat_pqb = %g %g %g Rc_hat_gsm = %g %g %g\n", Rc_hat_pqb.x, Rc_hat_pqb.y, Rc_hat_pqb.z, Rc_hat_gsm.x, Rc_hat_gsm.y, Rc_hat_gsm.z); + + + v_per_hat_pqb.x = -sin(phi*RadPerDeg); v_per_hat_pqb.y = cos(phi*RadPerDeg); v_per_hat_pqb.z = 0.0;; + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); // dont need to shift system for velocity + //printf("v_per_hat_pqb = %g %g %g v_per_hat_gsm = %g %g %g\n", v_per_hat_pqb.x, v_per_hat_pqb.y, v_per_hat_pqb.z, v_per_hat_gsm.x, v_per_hat_gsm.y, v_per_hat_gsm.z); + +printf("bhat dot v_per_hat_gsm = %g\n", Lgm_DotProduct( &bhat, &v_per_hat_gsm) ); + + + //See Hess or Anderson, 1966 (Annual Review of Nuc. Sci.) + Tc = 2.0*M_PI * p.m/fabs(p.q * p.B0*1e-9); + //printf("Approximate Cycoltron Period in Dipole: %g seconds\n", Tc ); + + /* + * We need to offset the starting position of the particle based on its gyroradius. + */ +printf("A. gamma = %lf\n", gamma_n); + gamma = 1.0/sqrt( 1.0 - v_tot*v_tot/p.c2); +printf("B. gamma = %lf\n", gamma); + Rc = fabs(gamma*p.m*v_per/(p.q*Blocal*1e-9)); // particle gyro-radius in meters + //printf("Blocal = %g v_per = %g p.q = %g Rc = %g\n", Blocal, v_per, p.q, Rc); + + + w_off.x = w0.x*1000.0*Re + Rc * Rc_hat_gsm.x; // m + w_off.y = w0.y*1000.0*Re + Rc * Rc_hat_gsm.y; // m + w_off.z = w0.z*1000.0*Re + Rc * Rc_hat_gsm.z; // m + //printf("w_off = %g %g %g\n", w_off.x/1000.0/Re, w_off.y/1000.0/Re, w_off.z/1000.0/Re); + + + + + + + + /* + * Recompute local b-field related quantities. + * + * We are shifting where the particle is (from the GC to its full orbit helix). + * A problem with this is that the b-hast is slightly different at this ne location. + * If we just use the previous b-hat and v-perp directions, thinks will be a bit off. + * (You have to be careful here or KE wont be conserved.) + */ + ww.x = w_off.x/1000.0/Re; ww.y = w_off.y/1000.0/Re; ww.z = w_off.z/1000.0/Re; + + // New (slightly different) b-hat. + p.mInfo->Bfield( &ww, &bhat, p.mInfo ); Blocal = Lgm_NormalizeVector( &bhat ); + + // New (slightly different) v_per_hat_pqb and v_per_hat_gsm vector. + Lgm_Set_GSM_TO_PQB( &ww, p.mInfo ); + Lgm_MatTimesVec( p.mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); + + // Recompute local pitch angle (because |Blocal| is slightly different). + p.Alpha = asin( sqrt( Blocal/B_mirror ) )*DegPerRad; + printf("Blocal, p.Alpha = %g, %g\n", Blocal, p.Alpha); + + // Recompute (slightly different partitionin of v_tot between v_par and v_per. + v_par = v_tot * cos( p.Alpha*RadPerDeg ); + v_per = v_tot * sin( p.Alpha*RadPerDeg ); + //printf("A. v_tot = %g\n", v_tot ) ; + //printf("B. v_tot = %g\n", sqrt( v_par*v_par + v_per*v_per ) ); + + + + + + + + // initial velocity vector u = gamma*v + //printf("v_par, v_per = %g %g Bvec = %g %g %g v_per_hat_gsm = %g %g %g\n", v_par, v_per, Bvec.x, Bvec.y, Bvec.z, v_per_hat_gsm.x, v_per_hat_gsm.y, v_per_hat_gsm.z); + u0.x = gamma*(v_par * bhat.x + v_per * v_per_hat_gsm.x); + u0.y = gamma*(v_par * bhat.y + v_per * v_per_hat_gsm.y); + u0.z = gamma*(v_par * bhat.z + v_per * v_per_hat_gsm.z); + //printf("bhat dot v_per_hat_gsm = %g Lgm_Magnitude( &v_per_hat_gsm ) = %g\n", Lgm_DotProduct( &bhat, &v_per_hat_gsm ), Lgm_Magnitude( &v_per_hat_gsm ) ); + + + +printf("C. v_tot, |u0|/gamma = %g %g\n", v_tot, Lgm_Magnitude( &u0 )/gamma ); +Lgm_Vector uuu0; +uuu0.x = v_par * bhat.x + v_per * v_per_hat_gsm.x; +uuu0.y = v_par * bhat.y + v_per * v_per_hat_gsm.y; +uuu0.z = v_par * bhat.z + v_per * v_per_hat_gsm.z; +printf("D. v_tot, |uuu0| = %g %g\n", v_tot, Lgm_Magnitude( &uuu0 ) ); +printf("|bhat| = %g\n", Lgm_Magnitude( &bhat ) ); +printf("|v_per_hat_gsm| = %g\n", Lgm_Magnitude( &v_per_hat_gsm ) ); +printf("E. v_tot = %g\n", sqrt( v_par*v_par +v_per*v_per ) ); + + + + + + double y[6]; + *nParticlePosition = 0; + ParticlePosition[0].x = w_off.x/1000.0/Re; + ParticlePosition[0].y = w_off.y/1000.0/Re; + ParticlePosition[0].z = w_off.z/1000.0/Re; + ++(*nParticlePosition); + y[0] = w_off.x; + y[1] = w_off.y; + y[2] = w_off.z; + y[3] = u0.x; + y[4] = u0.y; + y[5] = u0.z; + + + InitParticleIntegrators( &p ); + + + + + g = Lgm_Magnitude( &u0 ); + gamma_n = sqrt( 1.0 + g*g/p.c2 ); + printf("g = %g gamma, gamma_n = %lf %lf\n", g, gamma, gamma_n); + KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; + printf("g = %g , gamma_n = %lf , KineticEnergy_n = %g\n", g, gamma_n, KineticEnergy_n); + + + + + + // save initial position + //p.mInfo->Bfield( &ParticlePosition[0], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + //fprintf( fp, "%g %g %g %g %g %g\n", 0.0, ParticlePosition[0].x, ParticlePosition[0].y, ParticlePosition[0].z, Blocal, KineticEnergy_n ); + + + + int nSteps; + double ti, dT; + int i, status; + nSteps = 132000; + t = 0.0; + dT = 0.00001; + for ( i = 1; i <= nSteps; i++ ) { + ti = i*dT; + status = gsl_odeiv2_driver_apply( p.Driver_Full, &t, ti, y ); + if ( status != GSL_SUCCESS ) { + printf ("error, return value=%d\n", status); + break; + } + + + + double xxx, yyy, zzz; + xxx = y[0]/1000.0/Re; + yyy = y[1]/1000.0/Re; + zzz = y[2]/1000.0/Re; + if ( (xxx > 15.0) || (xxx < -200.0) || ( fabs( yyy ) > 30.0 ) || ( fabs( zzz ) > 30.0 ) ) { + printf("Particle lost: ParticlePosition[%d] = %g %g %g\n", i, xxx, yyy, zzz ); + break; + } + + un.x = y[3]; un.y = y[4]; un.z = y[5]; + g = Lgm_Magnitude( &un ); + gamma_n = sqrt( 1.0 + g*g/p.c2 ); + KineticEnergy_n = p.m*g*g/(gamma_n + 1.0)*6.241509e+15; + + + + if ((i%1 == 0)&&( *nParticlePosition<50000)){ + + ParticlePosition[ *nParticlePosition ].x = xxx; + ParticlePosition[ *nParticlePosition ].y = yyy; + ParticlePosition[ *nParticlePosition ].z = zzz; + + p.mInfo->Bfield( &ParticlePosition[ *nParticlePosition ], &Bvec, p.mInfo ); Blocal = Lgm_Magnitude( &Bvec ); + //fprintf( fp, "%g %g %g %g %g %g\n", t, xxx, yyy, zzz, Blocal, KineticEnergy_n ); + //fflush( fp ); + + ++(*nParticlePosition); + } + + } + + +} + + + +/* + * This sets up GSL ODE drivers for the Full Orbit and GCA + * equations. + */ + +int InitParticleIntegrators( Lgm_ParticleInfo *p ) { + + void *params; params = (void *)p; + + /* + * Setup ODE solver for 6D Full orbit equations in GSL + */ + p->Sys_Full.function = f_full; + p->Sys_Full.jacobian = J_full; + p->Sys_Full.dimension = 6; + p->Sys_Full.params = params; + p->Driver_Full = gsl_odeiv2_driver_alloc_y_new( &(p->Sys_Full), gsl_odeiv2_step_rk8pd, 1e-6, 1e-6, 0.0 ); + + + /* + * Setup ODE solver for 4D Guiding Center Approximation (GCA) equations in GSL + */ + p->Sys_GCA.function = f_gc; + p->Sys_GCA.jacobian = NULL; // dont use methods that require jacobian (not easy to compute) + p->Sys_GCA.dimension = 4; + p->Sys_GCA.params = params; + p->Driver_GCA = gsl_odeiv2_driver_alloc_y_new( &(p->Sys_GCA), gsl_odeiv2_step_rk8pd, 1e-6, 1e-6, 0.0 ); + + +} + + + + + +/* +To implement domain decomp on the time slices, we will need to push a particle +from ti -> ti+1, where these could be fairly large steps. I.e., they would be as +large as you can fit into the memory of a single core. If you have very high +spatial resolution, then the full files are pretty large. Further if we have +high time resolution, then we cant fit very many time slices in memory at once. +Thus we need to decompose the time over the full run. To do this, we could +allocate banks of processors to handle only certain time slices... Then we can +use the appropriate processors to push over the associated times... For +example, if we had NxM array of processors, we could reserve columms for +feeding particles in at the "top" The rows would be time intervals. I.e. row 1 +would be populated with processors that are all loaded with times slices to go +from t0->t1. Etc... When a particle finishes in row 1 it moves on the to next +row unmtil done.... +*/ + +/* + * Push a particle by a timestep dt (in seconds). + * + * The particle characteristics are all contained in the Lgm_ParticleInfo + * structure. + * + * The positions and velocities are also contained in the Lgm_ParticleInfo + * structure. + */ +int Lgm_PushParticle( double dT, Lgm_ParticleInfo *p ) { + + double y[6]; + int status; // error status from gsl routines + double t = 0.0; + + + if ( p->CurrentIntegrator == FULL_ORBIT ) { + + y[0] = p->x.x; y[1] = p->x.y; y[2] = p->x.z; // Full orbit particle position + y[3] = p->u.x; y[4] = p->u.y; y[5] = p->u.z; // full orbit particle gamma*v + status = gsl_odeiv2_driver_apply( p->Driver_Full, &t, dT, y ); + if ( status != GSL_SUCCESS ) { printf ("error, return value=%d\n", status); } + p->x.x = y[0]; p->x.y = y[1]; p->x.z = y[2]; + p->u.x = y[3]; p->u.y = y[4]; p->u.z = y[5]; + + } else if ( p->CurrentIntegrator == GCA ) { + + y[0] = p->X.x; y[1] = p->X.y; y[2] = p->X.z; // GCA position + y[3] = p->p_par; // GCA parallel momentum + status = gsl_odeiv2_driver_apply( p->Driver_GCA, &t, dT, y ); + p->X.x = y[0]; p->X.y = y[1]; p->X.z = y[2]; // GCA position + p->p_par = y[3]; + + } else { + printf( "Integrator not defined\n" ); + exit(0); + } + + + +} + + +/* + * + * KineticEnergy in keV + * + * + * + */ +int Lgm_InitParticle( int Species, double KineticEnergy, double Charge, double AlphaEq, Lgm_Vector *x0, Lgm_ParticleInfo *p ){ + + double v_tot, v_par, v_per; + long int Date; + double UTC; + double Ra, MLT, Phi, Lat; + Lgm_Vector u_sm, u0, w0; + int Flag; + Lgm_Vector v1, v2, v3, Bvec, bhat, B, E; + Lgm_Vector Rc_hat_pqb, Rc_hat_gsm, v_per_hat_pqb, v_per_hat_gsm; + Lgm_Vector w_sm, w_off, ww, un; + double SinAlpha, CosAlpha, g, Blocal, Beq, Tb_approx, t, dt, Rc, phi, gamma; + + double gamma_n, KineticEnergy_n; + + + p->KE = KineticEnergy/1000.0; // Initial Kinetic Energy, MeV + p->AlphaEq = AlphaEq; g = sin( AlphaEq*RadPerDeg ); // Initial equatorial pitch angle + + if ( Species == ELECTRON ) { + p->m = 9.10938291e-31; // electron mass, kg + p->q = Charge*1.60217657e-19; // Particle charge, C + } else if ( Species == PROTON ) { + p->m = 1.67262178e-27; // proton mass, kg + p->q = Charge*1.60217657e-19; // Particle charge, C + } + + + p->c = 2.99792458e8; // Speed of light, m/s + p->c2 = p->c * p->c; // c^2 + p->mc = p->m * p->c; // mc + p->mc2 = p->m * p->c * p->c; // mc^2 + + +// INIT_MISC +p->mInfo = Lgm_InitMagInfo( ); +p->mInfo->Kp = 2; +Lgm_MagModelInfo_Set_MagModel( LGM_IGRF, LGM_EXTMODEL_T89c, p->mInfo ); +//Lgm_MagModelInfo_Set_MagModel( LGM_CDIP, LGM_EXTMODEL_NULL, p->mInfo ); + +// UPDATE_TIME +Date = 20020418; +UTC = 5.0 + 30.0/60.0 + 0.0/3600.0; +Lgm_Set_Coord_Transforms( Date, UTC, p->mInfo->c ); + +u_sm = *x0; +printf("x0 = %g %g %g\n", x0->x, x0->y, x0->z); +Lgm_Convert_Coords( &u_sm, &u0, SM_TO_GSM, p->mInfo->c ); +Ra = 6.60000; +MLT = -1.5; +Phi = (MLT*15.0 - 180.0)*RadPerDeg; Lat = 0.0*RadPerDeg; +u_sm.x = Ra*cos( Phi )*cos(Lat); u_sm.y = Ra*sin( Phi )*cos(Lat); u_sm.z = Ra*sin(Lat); +Lgm_Convert_Coords( &u_sm, &u0, SM_TO_GSM, p->mInfo->c ); + +w0 = u0; +Flag = Lgm_Trace( &w0, &v1, &v2, &v3, 120.0, 1e-7, 1e-7, p->mInfo ); +p->mInfo->Bfield( &v3, &Bvec, p->mInfo ); Beq = Lgm_Magnitude( &Bvec ); +p->mInfo->Bfield( &w0, &Bvec, p->mInfo ); B = Bvec; bhat = Bvec; Blocal = Lgm_NormalizeVector( &bhat ); +//printf("Blocal, Beq = %g %g\n", Blocal, Beq); +B.x *= 1e-9; B.y *= 1e-9; B.z *= 1e-9; + + + // GCA position + p->X.x = u0.x*1000.0*Re; + p->X.y = u0.y*1000.0*Re; + p->X.z = u0.z*1000.0*Re; + + // GCA p_par +// this v_par is not right. +// We should have two tracks ...k + v_tot = sqrt( Lgm_v2overc2( p->KE, LGM_Ep0 ) ) * p->c; + SinAlpha = sqrt( Blocal/Beq * g*g ); + CosAlpha = sqrt( 1.0 - SinAlpha*SinAlpha); + v_par = v_tot * CosAlpha; + v_per = v_tot * SinAlpha; + gamma = 1.0/sqrt(1.0-v_tot*v_tot/p->c2); + p->p_par = gamma*p->m*v_par; + + + + + + + + + +p->B0 = Blocal; // Initial |B| in nT. +p->Alpha = asin( sqrt( Blocal/Beq * g*g ) )*DegPerRad; +g = sin( p->Alpha * RadPerDeg ); +double B_mirror = Blocal/(g*g); +v_par = v_tot * cos( p->Alpha*RadPerDeg ); +v_per = v_tot * sin( p->Alpha*RadPerDeg ); + +p->mu = Lgm_Ek_to_Mu( p->KE, p->Alpha, p->B0, LGM_Ep0 ); // mu in MeV/G +printf("E = %g MeV, Alpha = %g Degrees, B = %g nT, LGM_Ee0 = %g MeV Mu = %g MeV/G B_mirror = %g\n", p->KE, p->Alpha, p->B0, LGM_Ep0, p->mu, B_mirror ); +p->mu *= 1.60217657e-9; // mu in J/T + + +gamma_n = 1.0/sqrt(1.0-v_tot*v_tot/p->c2); +KineticEnergy_n = (gamma_n - 1.0)*p->m*p->c2*6.241509e+15; +printf("gamma_n, KE = %lf, %lf\n", gamma_n, KineticEnergy_n); + +double uuu; +uuu = v_tot*gamma_n; +KineticEnergy_n = p->m*uuu*uuu/(1.0+gamma_n)*6.241509e+15; +printf("uuu = %g gamma_n = %g KE = %lf\n", uuu, gamma_n, KineticEnergy_n); + + + + + + + + //printf("w0 = %g %g %g\n", w0.x, w0.y, w0.z); + Lgm_Set_GSM_TO_PQB( &w0, p->mInfo ); // This will set up the PQB coord system for this location + + /* + * Need to pick a phase angle in the perp plane to get where to offset particle + */ + phi = 90.0; Rc_hat_pqb.x = cos(phi*RadPerDeg); Rc_hat_pqb.y = sin(phi*RadPerDeg); Rc_hat_pqb.z = 0.0; + Lgm_MatTimesVec( p->mInfo->Apqb_to_gsm, &Rc_hat_pqb, &Rc_hat_gsm ); // dont need to shift system for velocity + //printf("Rc_hat_pqb = %g %g %g Rc_hat_gsm = %g %g %g\n", Rc_hat_pqb.x, Rc_hat_pqb.y, Rc_hat_pqb.z, Rc_hat_gsm.x, Rc_hat_gsm.y, Rc_hat_gsm.z); + + + v_per_hat_pqb.x = -sin(phi*RadPerDeg); v_per_hat_pqb.y = cos(phi*RadPerDeg); v_per_hat_pqb.z = 0.0;; + Lgm_MatTimesVec( p->mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); // dont need to shift system for velocity + //printf("v_per_hat_pqb = %g %g %g v_per_hat_gsm = %g %g %g\n", v_per_hat_pqb.x, v_per_hat_pqb.y, v_per_hat_pqb.z, v_per_hat_gsm.x, v_per_hat_gsm.y, v_per_hat_gsm.z); + + + + /* + * We need to offset the starting position of the particle based on its gyroradius. + */ + gamma = 1.0/sqrt( 1.0 - v_tot*v_tot/p->c2); + Rc = fabs(gamma*p->m*v_per/(p->q*Blocal*1e-9)); // particle gyro-radius in meters + + + w_off.x = w0.x*1000.0*Re + Rc * Rc_hat_gsm.x; // m + w_off.y = w0.y*1000.0*Re + Rc * Rc_hat_gsm.y; // m + w_off.z = w0.z*1000.0*Re + Rc * Rc_hat_gsm.z; // m + + + /* + * Recompute local b-field related quantities. + * + * We are shifting where the particle is (from the GC to its full orbit helix). + * A problem with this is that the b-hast is slightly different at this ne location. + * If we just use the previous b-hat and v-perp directions, thinks will be a bit off. + * (You have to be careful here or KE wont be conserved.) + */ + ww.x = w_off.x/1000.0/Re; ww.y = w_off.y/1000.0/Re; ww.z = w_off.z/1000.0/Re; + + // New (slightly different) b-hat. + p->mInfo->Bfield( &ww, &bhat, p->mInfo ); Blocal = Lgm_NormalizeVector( &bhat ); + + // New (slightly different) v_per_hat_pqb and v_per_hat_gsm vector. + Lgm_Set_GSM_TO_PQB( &ww, p->mInfo ); + Lgm_MatTimesVec( p->mInfo->Apqb_to_gsm, &v_per_hat_pqb, &v_per_hat_gsm ); + + // Recompute local pitch angle (because |Blocal| is slightly different). + + p->Alpha = asin( sqrt( Blocal/B_mirror ) )*DegPerRad; + printf("Blocal, p->Alpha = %g, %g\n", Blocal, p->Alpha); + + // Recompute (slightly different partitionin of v_tot between v_par and v_per. + v_par = v_tot * cos( p->Alpha*RadPerDeg ); + v_per = v_tot * sin( p->Alpha*RadPerDeg ); + + + // initial Full Orbit position + p->x.x = w_off.x; p->x.y = w_off.y; p->x.z = w_off.z; + + // initial Full Orbit velocity vector u = gamma*v + p->u.x = gamma*(v_par * bhat.x + v_per * v_per_hat_gsm.x); + p->u.y = gamma*(v_par * bhat.y + v_per * v_per_hat_gsm.y); + p->u.z = gamma*(v_par * bhat.z + v_per * v_per_hat_gsm.z); + + + + + +} + + + diff --git a/ViewDriftShell/PTM_MGH.c b/ViewDriftShell/PTM_MGH.c new file mode 100644 index 000000000..1ff0c536f --- /dev/null +++ b/ViewDriftShell/PTM_MGH.c @@ -0,0 +1,480 @@ +#include +#include +#include +#include +#include +#include +#include "ParticleInfo.h" + +#define TRUE 1 +#define FALSE 0 + +#define BUSY 1 +#define IDLE 0 + +#define NOT_STARTED 0 +#define SENT 1 +#define DONE 2 + + +/* + * Allocate memory that will be shared across the ranks in a node + * + * bufsize: This is the number of bytes to allocate + * buf: This is a (void *) pointer the the chunck of memory + */ +int AllocSharedMem( size_t bufsize, void **mem, MPI_Win *winbuf, int wrank, int nrank, MPI_Comm NodeComm ) { + + int localbufsize; + void *buf; + void *localbuf; + + int flag; + int *model; + MPI_Aint winsize; + int windisp; + int *winptr; + + /* + * This is a collective routine -- all of the members of NodeComm will call + * it. When calling MPI_Win_allocate_shared(), each member rank can + * contribute to the overall size of the buffer, or just one rank can + * allocate all the memory while the others contribute zero bytes. In + * either case, the end result is a shared chunk of memory. (It is + * possible that having all ask for some could be more efficient? Might + * depend on how memory is attached to the hardware the proc is running + * on.) + * + * For now, we will just have nrank=0 request the full size and all others + * will request 0 size. This is what localbufsize is for (it'll be full + * size for nrank=0, and zero for the other ranks.) + */ + localbufsize = ( nrank == 0 ) ? bufsize : 0; + MPI_Win_allocate_shared( localbufsize, 1, MPI_INFO_NULL, NodeComm, &localbuf, winbuf ); + MPI_Win_get_attr( *winbuf, MPI_WIN_MODEL, &model, &flag ); + + if ( flag != 1 ) { + printf("Attribute MPI_WIN_MODEL not defined\n"); + } else { + if (MPI_WIN_UNIFIED == *model) { + if ( wrank == 0 ) printf("Memory model is MPI_WIN_UNIFIED\n"); + } else { + if ( wrank == 0 ) printf("Memory model is *not* MPI_WIN_UNIFIED\n"); + MPI_Finalize(); + return 1; + } + } + // on nrank=0, buf is just the local buf + buf = localbuf; + // on other nranks, need to query to get the pointer we should use + if ( nrank != 0 ) { MPI_Win_shared_query( *winbuf, 0, &winsize, &windisp, &buf ); } + + *mem = buf; + + // All buf pointers should now point to copy on noderank 0 + MPI_Win_fence( 0, *winbuf ); // syncronize + +} + +int main(int argc, char** argv) { + + ParticleInfo JobList[1000]; + int wsize, wrank; + int nsize, nrank; + int mycolor; + + MPI_Status status; + MPI_Comm AllComm, NodeComm; + + double start, end; + + AllComm = MPI_COMM_WORLD; + + // Initialize the MPI environment + MPI_Init( &argc, &argv ); + + // Get the total number of processes and the world rank + MPI_Comm_size( AllComm, &wsize ); + MPI_Comm_rank( AllComm, &wrank ); + + + // Get the name of the processor + char processor_name[MPI_MAX_PROCESSOR_NAME]; + int name_len; + MPI_Get_processor_name(processor_name, &name_len); + + + // Split up the world communicator by shared memory. (I.e. by node) + MPI_Comm_split_type( AllComm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &NodeComm ); + MPI_Comm_size( NodeComm, &nsize ); // Number of processors in this shared mem group + MPI_Comm_rank( NodeComm, &nrank ); // My rank in this shared mem group + + if (nrank == 0 ) mycolor = wrank; // make the "color" be the wrank of the nrank=0 process. + //if ( nrank == 0 ){ printf("I am nrank = 0. I am setting mycolor to be my world rank (wrank = %d). I am broadcasting this to others in my NodeComm\n", wrank); } + MPI_Bcast( &mycolor, 1, MPI_INT, 0, NodeComm ); // Broadcast mycolor to others in this communicator + //if ( nrank != 0 ) { printf( "I am nrank = %d. I am receiving mycolor (mycolor = %d) (my wrank = %d).\n", nrank, mycolor, wrank ); } + MPI_Barrier( NodeComm ); + + + // at this point, every proc in my NodeComm will have mycolor set to the + // world rank of the local nrank=0 proc + // Let's Gather all of the colors into the root proc + int *ProcColors; + ProcColors = (int *)calloc( wsize, sizeof(int) ); + MPI_Gather( &mycolor, 1, MPI_INT, ProcColors, wsize, MPI_INT, 0, AllComm ); + + int i, j; + if ( wrank == 0 ){ + for ( i=0; iLastMin) && (c < Min ) ) Min = c; + //printf("c = %d, n = %d, LastMin = %d, Min = %d\n", c, n, LastMin, Min); + } + LastMin = Min; + //printf("\n"); + + if ( Min == INT_MAX ) { + // no values left to process -- we're done + done = TRUE; + break; + } else { + nProc[n] = 0; + for ( i=0; i= 2 ) { + /* + * No more jobs to send, but we couls still be weaiting for results. + */ + if ( nWorking == 0 ) { + // End worker procs + Job.ID = -1; + for ( i=0; i lumaMax)) return rgbA; + return rgbB; +} + + +vec4 PostFX(sampler2D tex, vec2 uv, float time) { + vec4 c = vec4(0.0); + vec2 rcpFrame = vec2(1.0/rt_w, 1.0/rt_h); + c.rgb = FxaaPixelShader(posPos, tex, rcpFrame); + //c.rgb = 1.0 - texture2D(tex, posPos.xy).rgb; + c.a = 1.0; + return c; +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_fragment.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_fragment.glsl new file mode 100644 index 000000000..a00b25368 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_fragment.glsl @@ -0,0 +1,17 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +uniform samplerRECT TempTex; + +void main(void) +{ + gl_FragColor = textureRect(TempTex, gl_FragCoord.xy); + // for occlusion query + if (gl_FragColor.a == 0) discard; +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_vertex.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_vertex.glsl new file mode 100644 index 000000000..2a199fd4f --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_blend_vertex.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_Position = gl_ModelViewMatrix * gl_Vertex; +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_final_fragment.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_final_fragment.glsl new file mode 100644 index 000000000..dc629cbc7 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_final_fragment.glsl @@ -0,0 +1,28 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +uniform samplerRECT DepthBlenderTex; +uniform samplerRECT FrontBlenderTex; +uniform samplerRECT BackBlenderTex; + +void main(void) +{ + vec4 frontColor = textureRect(FrontBlenderTex, gl_FragCoord.xy); + vec3 backColor = textureRect(BackBlenderTex, gl_FragCoord.xy).rgb; + float alphaMultiplier = 1.0 - frontColor.w; + + // front + back + gl_FragColor.rgb = frontColor + backColor * alphaMultiplier; + + // front blender + //gl_FragColor.rgb = frontColor + vec3(alphaMultiplier); + + // back blender + //gl_FragColor.rgb = backColor; +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_final_vertex.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_final_vertex.glsl new file mode 100644 index 000000000..30aff4510 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_final_vertex.glsl @@ -0,0 +1,14 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + //gl_Position = gl_ModelViewMatrix * gl_Vertex; + gl_Position = ftransform(); +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_init_fragment.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_init_fragment.glsl new file mode 100644 index 000000000..fea8906bf --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_init_fragment.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_FragColor.xy = vec2(-gl_FragCoord.z, gl_FragCoord.z); +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_init_vertex.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_init_vertex.glsl new file mode 100644 index 000000000..785f9a4b5 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_init_vertex.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_Position = ftransform(); +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_fragment.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_fragment.glsl new file mode 100644 index 000000000..55b4eb823 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_fragment.glsl @@ -0,0 +1,94 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +#extension ARB_draw_buffers : require + +uniform samplerRECT DepthBlenderTex; +uniform samplerRECT FrontBlenderTex; + +#define MAX_DEPTH 1.0 + + + + +uniform float Alpha; + +#define COLOR_FREQ 30.0 +#define ALPHA_FREQ 30.0 + +vec4 ShadeFragment() { + + float xWorldPos = gl_TexCoord[0].x; + float yWorldPos = gl_TexCoord[0].y; + float diffuse = gl_TexCoord[0].z; + + vec4 color; + float i = floor(xWorldPos * COLOR_FREQ); + float j = floor(yWorldPos * ALPHA_FREQ); + color.rgb = (fmod(i, 2.0) == 0) ? vec3(.4,.85,.0) : vec3(1.0); + //color.a = (fmod(j, 2.0) == 0) ? Alpha : 0.2; + Alpha = 1.0; // mgh + color.a = Alpha; + + + color.rgb *= diffuse; + return color; +} + + +void main( void ) { + + // window-space depth interpolated linearly in screen space + float fragDepth = gl_FragCoord.z; + + vec2 depthBlender = textureRect(DepthBlenderTex, gl_FragCoord.xy).xy; + vec4 forwardTemp = textureRect(FrontBlenderTex, gl_FragCoord.xy); + + // Depths and 1.0-alphaMult always increase + // so we can use pass-through by default with MAX blending + gl_FragData[0].xy = depthBlender; + + // Front colors always increase (DST += SRC*ALPHA_MULT) + // so we can use pass-through by default with MAX blending + gl_FragData[1] = forwardTemp; + + // Because over blending makes color increase or decrease, + // we cannot pass-through by default. + // Each pass, only one fragment writes a color greater than 0 + gl_FragData[2] = vec4(0.0); + + float nearestDepth = -depthBlender.x; + float farthestDepth = depthBlender.y; + float alphaMultiplier = 1.0 - forwardTemp.w; + + if (fragDepth < nearestDepth || fragDepth > farthestDepth) { + // Skip this depth in the peeling algorithm + gl_FragData[0].xy = vec2(-MAX_DEPTH); + return; + } + + if (fragDepth > nearestDepth && fragDepth < farthestDepth) { + // This fragment needs to be peeled again + gl_FragData[0].xy = vec2(-fragDepth, fragDepth); + return; + } + + // If we made it here, this fragment is on the peeled layer from last pass + // therefore, we need to shade it, and make sure it is not peeled any farther + vec4 color = ShadeFragment(); + gl_FragData[0].xy = vec2(-MAX_DEPTH); + + if (fragDepth == nearestDepth) { + gl_FragData[1].xyz += color.rgb * color.a * alphaMultiplier; + gl_FragData[1].w = 1.0 - alphaMultiplier * (1.0 - color.a); + } else { + gl_FragData[2] += color; + } + +} diff --git a/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_vertex.glsl b/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_vertex.glsl new file mode 100644 index 000000000..7b39bafd6 --- /dev/null +++ b/ViewDriftShell/Shaders/SAFE/dual_peeling_peel_vertex.glsl @@ -0,0 +1,19 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +vec3 ShadeVertex() { + float diffuse = abs( normalize( gl_NormalMatrix * gl_Normal ).z ); + return vec3( gl_Vertex.xy, diffuse ); +} + + +void main( void ) { + gl_Position = ftransform(); + gl_TexCoord[0].xyz = ShadeVertex(); +} diff --git a/ViewDriftShell/Shaders/dual_peeling_blend_fragment.glsl b/ViewDriftShell/Shaders/dual_peeling_blend_fragment.glsl new file mode 100644 index 000000000..a00b25368 --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_blend_fragment.glsl @@ -0,0 +1,17 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +uniform samplerRECT TempTex; + +void main(void) +{ + gl_FragColor = textureRect(TempTex, gl_FragCoord.xy); + // for occlusion query + if (gl_FragColor.a == 0) discard; +} diff --git a/ViewDriftShell/Shaders/dual_peeling_blend_vertex.glsl b/ViewDriftShell/Shaders/dual_peeling_blend_vertex.glsl new file mode 100644 index 000000000..2a199fd4f --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_blend_vertex.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_Position = gl_ModelViewMatrix * gl_Vertex; +} diff --git a/ViewDriftShell/Shaders/dual_peeling_final_fragment.glsl b/ViewDriftShell/Shaders/dual_peeling_final_fragment.glsl new file mode 100644 index 000000000..dc629cbc7 --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_final_fragment.glsl @@ -0,0 +1,28 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +uniform samplerRECT DepthBlenderTex; +uniform samplerRECT FrontBlenderTex; +uniform samplerRECT BackBlenderTex; + +void main(void) +{ + vec4 frontColor = textureRect(FrontBlenderTex, gl_FragCoord.xy); + vec3 backColor = textureRect(BackBlenderTex, gl_FragCoord.xy).rgb; + float alphaMultiplier = 1.0 - frontColor.w; + + // front + back + gl_FragColor.rgb = frontColor + backColor * alphaMultiplier; + + // front blender + //gl_FragColor.rgb = frontColor + vec3(alphaMultiplier); + + // back blender + //gl_FragColor.rgb = backColor; +} diff --git a/ViewDriftShell/Shaders/dual_peeling_final_vertex.glsl b/ViewDriftShell/Shaders/dual_peeling_final_vertex.glsl new file mode 100644 index 000000000..2a199fd4f --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_final_vertex.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_Position = gl_ModelViewMatrix * gl_Vertex; +} diff --git a/ViewDriftShell/Shaders/dual_peeling_init_fragment.glsl b/ViewDriftShell/Shaders/dual_peeling_init_fragment.glsl new file mode 100644 index 000000000..fea8906bf --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_init_fragment.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_FragColor.xy = vec2(-gl_FragCoord.z, gl_FragCoord.z); +} diff --git a/ViewDriftShell/Shaders/dual_peeling_init_vertex.glsl b/ViewDriftShell/Shaders/dual_peeling_init_vertex.glsl new file mode 100644 index 000000000..785f9a4b5 --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_init_vertex.glsl @@ -0,0 +1,13 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +void main(void) +{ + gl_Position = ftransform(); +} diff --git a/ViewDriftShell/Shaders/dual_peeling_peel_fragment.glsl b/ViewDriftShell/Shaders/dual_peeling_peel_fragment.glsl new file mode 100644 index 000000000..f4f2e62ef --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_peel_fragment.glsl @@ -0,0 +1,105 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +#extension ARB_draw_buffers : require + +uniform samplerRECT DepthBlenderTex; +uniform samplerRECT FrontBlenderTex; + +uniform vec3 LightPos; +uniform vec3 CameraPos; + +#define MAX_DEPTH 1.0 + +varying vec3 N; +varying vec3 v; + + +vec4 ShadeFragment( void ) { + + //vec3 L = normalize(gl_LightSource[0].position.xyz - v); + //vec3 E = normalize(vec3(10,0,0)-v); // we are in Eye Coordinates, so EyePos is (0,0,0) + //vec3 R = normalize(-reflect(L,N)); + + vec3 L = normalize( LightPos - v ); + vec3 E = normalize( CameraPos - v ); + vec3 R = normalize( -reflect( L,normalize(N) ) ); + + //calculate Ambient Term: + vec4 Iamb = gl_FrontLightProduct[0].ambient; + + //calculate Diffuse Term: + //vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); + vec4 Idiff = gl_FrontLightProduct[0].diffuse * abs(dot(N,L)); + //vec4 Idiff = gl_FrontLightProduct[0].diffuse * dot(N,L); + Idiff = clamp(Idiff, 0.0, 1.0); + + // calculate Specular Term: + vec4 Ispec = gl_FrontLightProduct[0].specular * pow(dot(R,E),gl_FrontMaterial.shininess); + Ispec = clamp(Ispec, 0.0, 1.0); + + // write Total Color: + vec4 Itotal = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec; + //Itotal.a = gl_FrontLightProduct[0].diffuse.a; + + + return Itotal; +} + + +void main(void) { + + // window-space depth interpolated linearly in screen space + float fragDepth = gl_FragCoord.z; + + vec2 depthBlender = textureRect(DepthBlenderTex, gl_FragCoord.xy).xy; + vec4 forwardTemp = textureRect(FrontBlenderTex, gl_FragCoord.xy); + + // Depths and 1.0-alphaMult always increase + // so we can use pass-through by default with MAX blending + gl_FragData[0].xy = depthBlender; + + // Front colors always increase (DST += SRC*ALPHA_MULT) + // so we can use pass-through by default with MAX blending + gl_FragData[1] = forwardTemp; + + // Because over blending makes color increase or decrease, + // we cannot pass-through by default. + // Each pass, only one fragment writes a color greater than 0 + gl_FragData[2] = vec4(0.0); + + float nearestDepth = -depthBlender.x; + float farthestDepth = depthBlender.y; + float alphaMultiplier = 1.0 - forwardTemp.w; + + if (fragDepth < nearestDepth || fragDepth > farthestDepth) { + // Skip this depth in the peeling algorithm + gl_FragData[0].xy = vec2(-MAX_DEPTH); + return; + } + + if (fragDepth > nearestDepth && fragDepth < farthestDepth) { + // This fragment needs to be peeled again + gl_FragData[0].xy = vec2(-fragDepth, fragDepth); + return; + } + + // If we made it here, this fragment is on the peeled layer from last pass + // therefore, we need to shade it, and make sure it is not peeled any farther + vec4 color = ShadeFragment(); +color.a = 1.00; + gl_FragData[0].xy = vec2(-MAX_DEPTH); + + if (fragDepth == nearestDepth) { + gl_FragData[1].xyz += color.rgb * color.a * alphaMultiplier; + gl_FragData[1].w = 1.0 - alphaMultiplier * (1.0 - color.a); + } else { + gl_FragData[2] += color; + } +} diff --git a/ViewDriftShell/Shaders/dual_peeling_peel_vertex.glsl b/ViewDriftShell/Shaders/dual_peeling_peel_vertex.glsl new file mode 100644 index 000000000..112e509fc --- /dev/null +++ b/ViewDriftShell/Shaders/dual_peeling_peel_vertex.glsl @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Dual Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +varying vec3 N; +varying vec3 v; + +void main(void) { + + //v = vec3(gl_ModelViewMatrix * gl_Vertex); + //N = normalize(gl_NormalMatrix * gl_Normal); + v = gl_Vertex; + N = gl_Normal; + + //gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = ftransform(); + +} + diff --git a/ViewDriftShell/Shaders/front_peeling_peel_fragment.glsl b/ViewDriftShell/Shaders/front_peeling_peel_fragment.glsl new file mode 100644 index 000000000..0c4e73265 --- /dev/null +++ b/ViewDriftShell/Shaders/front_peeling_peel_fragment.glsl @@ -0,0 +1,25 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +uniform samplerRECT DepthTex; + +vec4 ShadeFragment(); + +void main(void) +{ + // Bit-exact comparison between FP32 z-buffer and fragment depth + float frontDepth = textureRect(DepthTex, gl_FragCoord.xy).r; + if (gl_FragCoord.z <= frontDepth) { + discard; + } + + // Shade all the fragments behind the z-buffer + vec4 color = ShadeFragment(); + gl_FragColor = vec4(color.rgb * color.a, color.a); +} diff --git a/ViewDriftShell/Shaders/front_peeling_peel_vertex.glsl b/ViewDriftShell/Shaders/front_peeling_peel_vertex.glsl new file mode 100644 index 000000000..5b67d997a --- /dev/null +++ b/ViewDriftShell/Shaders/front_peeling_peel_vertex.glsl @@ -0,0 +1,16 @@ +//-------------------------------------------------------------------------------------- +// Order Independent Transparency with Depth Peeling +// +// Author: Louis Bavoil +// Email: sdkfeedback@nvidia.com +// +// Copyright (c) NVIDIA Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- + +vec3 ShadeVertex(); + +void main(void) +{ + gl_Position = ftransform(); + gl_TexCoord[0].xyz = ShadeVertex(); +} diff --git a/ViewDriftShell/Shaders/shade_fragment.glsl b/ViewDriftShell/Shaders/shade_fragment.glsl new file mode 100644 index 000000000..c9647cb31 --- /dev/null +++ b/ViewDriftShell/Shaders/shade_fragment.glsl @@ -0,0 +1,25 @@ +varying vec3 N; +varying vec3 v; + +vec4 ShadeFragment( void ) { + + vec3 L = normalize(gl_LightSource[0].position.xyz - v); + vec3 E = normalize(-v); // we are in Eye Coordinates, so EyePos is (0,0,0) + vec3 R = normalize(-reflect(L,N)); + + //calculate Ambient Term: + vec4 Iamb = gl_FrontLightProduct[0].ambient; + + //calculate Diffuse Term: + vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); + Idiff = clamp(Idiff, 0.0, 1.0); + + // calculate Specular Term: + vec4 Ispec = gl_FrontLightProduct[0].specular + * pow(max(dot(R,E),0.0),0.3*gl_FrontMaterial.shininess); + Ispec = clamp(Ispec, 0.0, 1.0); + + // write Total Color: + return gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec; +} + diff --git a/ViewDriftShell/Shaders/shade_vertex.glsl b/ViewDriftShell/Shaders/shade_vertex.glsl new file mode 100644 index 000000000..9df4a33cc --- /dev/null +++ b/ViewDriftShell/Shaders/shade_vertex.glsl @@ -0,0 +1,11 @@ +varying vec3 N; +varying vec3 v; + +void ShadeVertex(void) { + + v = vec3(gl_ModelViewMatrix * gl_Vertex); + N = normalize(gl_NormalMatrix * gl_Normal); + + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + +} diff --git a/libLanlGeoMag/ComputeI_FromMltMlat.c b/libLanlGeoMag/ComputeI_FromMltMlat.c index c1c1f634b..dff85d26d 100644 --- a/libLanlGeoMag/ComputeI_FromMltMlat.c +++ b/libLanlGeoMag/ComputeI_FromMltMlat.c @@ -9,15 +9,15 @@ * 2. The new method seacrhes for Bm along a FL launched traced from the surface at the given MLT/MLAT. * */ -double ComputeI_FromMltMlat( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ) { +double ComputeI_FromMltMlat( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ) { if ( LstarInfo->ISearchMethod == 1 ) { - return( ComputeI_FromMltMlat1( Bm, MLT, mlat, r, I0, LstarInfo ) ); + return( ComputeI_FromMltMlat1( Bm, MLT, mlat, r, I0, ErrorStatus, LstarInfo ) ); } else if ( LstarInfo->ISearchMethod == 2 ) { - return( ComputeI_FromMltMlat2( Bm, MLT, mlat, r, I0, LstarInfo ) ); + return( ComputeI_FromMltMlat2( Bm, MLT, mlat, r, I0, ErrorStatus, LstarInfo ) ); } else { @@ -30,7 +30,7 @@ double ComputeI_FromMltMlat( double Bm, double MLT, double mlat, double *r, doub -double ComputeI_FromMltMlat1( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ) { +double ComputeI_FromMltMlat1( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ) { int reset=1, reset2; diff --git a/libLanlGeoMag/ComputeI_FromMltMlat2.c b/libLanlGeoMag/ComputeI_FromMltMlat2.c index d9b73601f..d061647ca 100644 --- a/libLanlGeoMag/ComputeI_FromMltMlat2.c +++ b/libLanlGeoMag/ComputeI_FromMltMlat2.c @@ -6,7 +6,7 @@ /* * This version traces FLs from the Earth at the given MLT/mlat instead of trying to find the Bm radially out first. */ -double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ) { +double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ) { int reset=1, reset2, TraceFlag; @@ -15,6 +15,8 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou double stmp, Btmp; + // Assume its good to start + *ErrorStatus = 1; @@ -28,6 +30,18 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou Lgm_Convert_Coords( &w, &u, SM_TO_GSM, LstarInfo->mInfo->c ); TraceFlag = Lgm_Trace( &u, &v1, &v2, &v3, LstarInfo->mInfo->Lgm_LossConeHeight, TRACE_TOL, TRACE_TOL, LstarInfo->mInfo ); +LstarInfo->mInfo->Pmin = v3; +//LstarInfo->mInfo->Bfield( &u, &Bvec, LstarInfo->mInfo ); +//printf("u = %g %g %g |B| = %g\n", u.x, u.y, u.z, Lgm_Magnitude( &Bvec ) ); +// +//LstarInfo->mInfo->Bfield( &v1, &Bvec, LstarInfo->mInfo ); +//printf("v1 = %g %g %g |B| = %g\n", v1.x, v1.y, v1.z, Lgm_Magnitude( &Bvec ) ); +// +//LstarInfo->mInfo->Bfield( &v2, &Bvec, LstarInfo->mInfo ); +//printf("v2 = %g %g %g |B| = %g\n", v2.x, v2.y, v2.z, Lgm_Magnitude( &Bvec ) ); +// +//LstarInfo->mInfo->Bfield( &v3, &Bvec, LstarInfo->mInfo ); +//printf("v3 = %g %g %g |B| = %g\n", v3.x, v3.y, v3.z, Lgm_Magnitude( &Bvec ) ); LstarInfo->mInfo->Bfield( &v3, &Bvec, LstarInfo->mInfo ); Bmin = Lgm_Magnitude( &Bvec ); @@ -39,6 +53,8 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%s mlat: %13.6g I: %13.6g I0: %13.6g > Field Line not closed%s\n", LstarInfo->PreStr, mlat, I, I0, LstarInfo->PostStr ); } + + *ErrorStatus = -1; // FL open. I undefined. return( I ); } else if ( Bmin <= Bm ) { @@ -62,12 +78,12 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou //printf("Pmirror2 = %g %g %g\n", Pmirror2.x, Pmirror2.y, Pmirror2.z); } else { - return( 9e99 ); I = 9e99; if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%s mlat: %13.6g I: %13.6g I0: %13.6g > Unable to find southern mirror point%s\n", LstarInfo->PreStr, mlat, I, I0, LstarInfo->PostStr ); } - return( I ); + *ErrorStatus = -3; // No valid mirror point in the south + return( I ); } // total distance between mirror point. @@ -79,6 +95,7 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%s mlat: %13.6g I: %13.6g I0: %13.6g > Distance between mirror points is < 1e-7Re, Assuming I=0%s\n", LstarInfo->PreStr, mlat, I, I0, LstarInfo->PostStr ); } + *ErrorStatus = 2; // Flag that we did this return( I ); } @@ -87,11 +104,12 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%s mlat: %13.6g I: %13.6g I0: %13.6g > Unable to find northern mirror point%s\n", LstarInfo->PreStr, mlat, I, I0, LstarInfo->PostStr ); } + *ErrorStatus = -2; // No valid mirror point in the north return( I ); } /* - * OK, we have both mirror points. LEts compute I + * OK, we have both mirror points. Lets compute I */ I = 9e99; LstarInfo->mInfo->Hmax = 0.1; @@ -138,15 +156,23 @@ double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, dou } else { I = 9e99; - if (LstarInfo->VerbosityLevel > 1) { + if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%s mlat: %13.6g I: %13.6g I0: %13.6g Couldnt initialize spline%s\n", LstarInfo->PreStr, mlat, I, I0, LstarInfo->PostStr ); - } + } } } else { - if (LstarInfo->VerbosityLevel > 1){ printf( "\t\t\t> Field line min-B is greater than Bm. Bm = %g Bmin = %g\n", Bm, Bmin ); } +//This seems problematic. +//I see the brackets being affected by this, but often in the wrong mdirection! + + if (LstarInfo->VerbosityLevel > 1){ printf( "\t\t\t> Field line min-B is greater than Bm. mlat = %g MLT = %g Bm = %g Bmin = %g (u = %g %g %g v1 = %g %g %g v2 = %g %g %g v3 = %g %g %g)\n", mlat, MLT, Bm, Bmin, u.x, u.y, u.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z ); } + + *ErrorStatus = -10; // We didnt even try to compute I because we Field line min-B is greater than Bm. + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%s mlat: %13.6g I: undefined I0: %13.6g I undefined. min-B is greater than Bm.%s\n", LstarInfo->PreStr, mlat, I0, LstarInfo->PostStr ); + } return( 9e99 ); } diff --git a/libLanlGeoMag/ComputeLstar.c b/libLanlGeoMag/ComputeLstar.c index d4c0613ee..d25dd56a1 100644 --- a/libLanlGeoMag/ComputeLstar.c +++ b/libLanlGeoMag/ComputeLstar.c @@ -36,9 +36,10 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { int i, iMin, Type, iMax, done, Verbosity, nBounceRegions; double Min, Max, Curr, Prev; - double Diff, OldDiff, Minima[ LGM_LSTARINFO_MAX_FL ], Maxima[ LGM_LSTARINFO_MAX_FL ]; - int nMinima, iMinima[ LGM_LSTARINFO_MAX_FL ]; - int nMaxima, iMaxima[ LGM_LSTARINFO_MAX_FL ]; + double Diff, OldDiff, b1, b2; + double Minima[ LGM_LSTARINFO_MAX_MINIMA ], Maxima[ LGM_LSTARINFO_MAX_MINIMA ]; + int nMinima, iMinima[ LGM_LSTARINFO_MAX_MINIMA ]; + int nMaxima, iMaxima[ LGM_LSTARINFO_MAX_MINIMA ]; Verbosity = LstarInfo->VerbosityLevel; m = LstarInfo->mInfo; @@ -65,28 +66,30 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { */ nMinima = 0; nMaxima = 0; - nBounceRegions=0; + nBounceRegions = 0; Diff = LGM_FILL_VALUE; for ( i=1; inPnts; i++ ){ OldDiff = Diff; Diff = m->Bmag[i] - m->Bmag[i-1]; - if ( (Diff > 0.0) && (OldDiff < 0.0) && (nMinima < LGM_LSTARINFO_MAX_MINIMA ) ) { - iMinima[nMinima] = i-1; - Minima[nMinima] = m->Bmag[i-1]; - ++nMinima; - } + if ( i>1 ) { + if ( (Diff > 0.0) && (OldDiff < 0.0) && (nMinima < LGM_LSTARINFO_MAX_MINIMA ) ) { + iMinima[nMinima] = i-1; + Minima[nMinima] = m->Bmag[i-1]; + ++nMinima; + } - if ( (Diff < 0.0) && (OldDiff > 0.0) && (nMaxima < LGM_LSTARINFO_MAX_MINIMA ) ) { - iMaxima[nMaxima] = i-1; - Maxima[nMaxima] = m->Bmag[i-1]; -//printf("m->Bmag[i-1], m->Bmag[i] = %g %g\n", m->Bmag[i-1], m->Bmag[i]); - ++nMaxima; + if ( (Diff < 0.0) && (OldDiff > 0.0) && (nMaxima < LGM_LSTARINFO_MAX_MINIMA ) ) { + iMaxima[nMaxima] = i-1; + Maxima[nMaxima] = m->Bmag[i-1]; + printf("m->Bmag[i-1], m->Bmag[i] = %g %g\n", m->Bmag[i-1], m->Bmag[i]); + ++nMaxima; + } } } -//Verbosity = 3; +Verbosity = 3; if ( Verbosity > 1 ) { if ( nMinima > 1 ) { @@ -103,11 +106,11 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { } - if ( nMaxima > 1 ) { + if ( nMaxima > 0 ) { if ( nMinima >= LGM_LSTARINFO_MAX_MINIMA-1 ) { printf("\t\tThis Field line has multiple maxima (at least %d maxima detected). Probably on a bizarre field line (TS04 model?)\n", nMaxima ); } else { - printf("\t\tThis Field line has multiple maxima (%d minima detected excluding endpoints). Probably on Shabansky orbit!\n", nMaxima ); + printf("\t\tThis Field line has multiple maxima (%d maxima detected excluding endpoints). Probably on Shabansky orbit!\n", nMaxima ); } if ( Verbosity > 2 ) { for ( i=0; inMinima[k] = nMinima; LstarInfo->nMaxima[k] = nMaxima; +//printf("nMinima, nMaxima = %d %d\n", nMinima, nMaxima ); +//printf("iMaxima[0] = %g\n", iMaxima[0]); if ( nMinima == 0 ) { // can this ever happen??? Type = -9; } else if ( nMinima == 1 ) { - Type = 0; + // normal FL + Type = 0; + + // can only bounce in 1 region. + nBounceRegions = 1; } else if ( (nMinima == 2) && (nMaxima == 1) && (iMinima[0] < iMaxima[0]) && (iMinima[1] > iMaxima[0]) ) { // typical expected case for Shab. - if ( Maxima[0] < m->Bm ) { + if ( Maxima[0] <= m->Bm ) { // can only bounce in 1 region. nBounceRegions = 1; @@ -145,8 +154,10 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { } else { // can potentially bounce in 2 regions. + // this checks for sure that Bm will allow it. Seems like an exotic case -- ignore for now... if ( Minima[0] <= m->Bm ) ++nBounceRegions; if ( Minima[1] <= m->Bm ) ++nBounceRegions; + //nBounceRegions = 2; } @@ -168,14 +179,31 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { * 2) minima that drop below Bm on either side. * The number of mirroring regions should be this number plus one. */ - nBounceRegions = 1; // there should be at least one region.... - for ( i=0; iBm) && (Maxima[i] > m->Bm) && (Minima[i+1] <= m->Bm) ) { + nBounceRegions = 0; + for ( i=0; iBm) && (b2 > m->Bm) ) { + // From this min to the max, we cross the Bm value -- this min + // supports mirroring. ++nBounceRegions; } + } - Type = nBounceRegions; + // add 10 so we know we had this case +//printf("nMinima = %d\n", nMinima); + Type = 10+nBounceRegions; +nBounceRegions = 1000; } @@ -190,6 +218,8 @@ int ClassifyFL( int k, Lgm_LstarInfo *LstarInfo ) { //} + LstarInfo->nBounceRegions[k] = nBounceRegions; + return( Type ); @@ -221,8 +251,8 @@ void Lgm_SetLstarTolerances( int Quality, int nFLsInDriftShell, Lgm_LstarInfo *s s->LstarQuality = Quality; } - if ( ( nFLsInDriftShell < 6 ) || ( nFLsInDriftShell > 240 ) ) { - printf("%sLgm_SetLstarTolerances: nFLsInDriftShell value (of %d) not in range [6, 240]. Setting to 24.%s\n", s->PreStr, nFLsInDriftShell, s->PostStr ); + if ( ( nFLsInDriftShell < 6 ) || ( nFLsInDriftShell >= LGM_LSTARINFO_MAX_FL ) ) { + printf("%sLgm_SetLstarTolerances: nFLsInDriftShell value (of %d) not in range [6, %d]. Setting to 24.%s\n", s->PreStr, nFLsInDriftShell, LGM_LSTARINFO_MAX_FL, s->PostStr ); s->nFLsInDriftShell = 24; } else { s->nFLsInDriftShell = nFLsInDriftShell; @@ -462,11 +492,11 @@ void Lgm_InitLstarInfoDefaults( Lgm_LstarInfo *LstarInfo ) { /* * Default Settings */ - LstarInfo->VerbosityLevel = 2; - LstarInfo->LSimpleMax = 10.0; - LstarInfo->ISearchMethod = 1; + LstarInfo->VerbosityLevel = 2; + LstarInfo->LSimpleMax = 10.0; + LstarInfo->ISearchMethod = 1; LstarInfo->ShabanskyHandling = LGM_SHABANSKY_IGNORE; - LstarInfo->LstarMoment = LGM_LSTAR_MOMENT_CDIP_2010; + LstarInfo->LstarMoment = LGM_LSTAR_MOMENT_CDIP_2010; LstarInfo->PreStr[0] = '\0'; LstarInfo->PostStr[0] = '\0'; @@ -634,7 +664,7 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ LstarInfo->mInfo->Bm = LstarInfo->mInfo->Blocal/sa2; // set Bmirror for supplied location, pitch angle, field model, etc. if (LstarInfo->VerbosityLevel > 1) { - printf("\n\t\t%sMin-B Point Location, Pmin (Re): < %g, %g, %g >%s\n", PreStr, LstarInfo->mInfo->Pmin.x, LstarInfo->mInfo->Pmin.y, LstarInfo->mInfo->Pmin.z, PostStr); + printf("\n\t\t%sMin-B Point Location, Pmin (Re): < %g, %g, %g >%s\n", PreStr, LstarInfo->mInfo->Pmin.x, LstarInfo->mInfo->Pmin.y, LstarInfo->mInfo->Pmin.z, PostStr); LstarInfo->mInfo->Bfield( &u, &Bvec, LstarInfo->mInfo ); B = Lgm_Magnitude( &Bvec ); printf("\t\t%sMag. Field Strength, B at Pmin (nT): %g%s\n", PreStr, B, PostStr); @@ -643,11 +673,11 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ if (LstarInfo->VerbosityLevel > 0) { printf("\n\n\t\t%s Computing L* for, Date: %ld, UT: %g%s\n", PreStr, LstarInfo->mInfo->c->UTC.Date, LstarInfo->mInfo->c->UTC.Time, PostStr ); printf( "\t\t%s=========================================================================%s\n", PreStr, PostStr ); - printf( "\t\t%sDate (yyyymmdd): %ld%s\n", PreStr, LstarInfo->mInfo->c->UTC.Date, PostStr ); - printf( "\t\t%sUTC (hours): %g%s\n", PreStr, LstarInfo->mInfo->c->UTC.Time, PostStr ); - printf( "\t\t%sPitch Angle (deg.): %g%s\n", PreStr, LstarInfo->PitchAngle, PostStr ); - printf( "\t\t%sInitial Position, vin (Re): < %g, %g, %g >%s\n", PreStr, vin->x, vin->y, vin->z, PostStr); - printf( "\t\t%sMirror Mag. Field Strength, Bm (nT): %g%s\n", PreStr, LstarInfo->mInfo->Bm, PostStr ); + printf( "\t\t%sDate (yyyymmdd): %ld%s\n", PreStr, LstarInfo->mInfo->c->UTC.Date, PostStr ); + printf( "\t\t%sUTC (hours): %g%s\n", PreStr, LstarInfo->mInfo->c->UTC.Time, PostStr ); + printf( "\t\t%sPitch Angle (deg.): %g%s\n", PreStr, LstarInfo->PitchAngle, PostStr ); + printf( "\t\t%sInitial Position, vin (Re): < %g, %g, %g >%s\n", PreStr, vin->x, vin->y, vin->z, PostStr); + printf( "\t\t%sMirror Mag. Field Strength, Bm (nT): %g%s\n", PreStr, LstarInfo->mInfo->Bm, PostStr ); } /* * If we are here, we know the field line is closed. @@ -739,7 +769,7 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ */ I = Iinv_interped( LstarInfo->mInfo ); if (LstarInfo->VerbosityLevel > 1) { - printf("\t\t %sIntegral Invariant, I (interped): %g%s\n", PreStr, I, PostStr ); + printf("\t\t %sIntegral Invariant, I (interped): %g%s\n", PreStr, I, PostStr ); } /* @@ -754,7 +784,7 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ LstarInfo->SbIntegral0 = SbIntegral_interped( LstarInfo->mInfo ); if (LstarInfo->VerbosityLevel > 1) { printf("\t\t %sSb Integral Equatorially Mirroring: %g%s\n", PreStr, LstarInfo->Sb0, PostStr ); - printf("\t\t %sSb Integral, (interped): %g%s\n", PreStr, LstarInfo->SbIntegral0, PostStr ); + printf("\t\t %sSb Integral, (interped): %g%s\n", PreStr, LstarInfo->SbIntegral0, PostStr ); } } FreeSpline( LstarInfo->mInfo ); @@ -776,13 +806,13 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ Ifound = I; if (LstarInfo->VerbosityLevel > 1) { - printf("\t\t %sLgm_n_I_integrand_Calls: %d%s\n\n", PreStr, LstarInfo->mInfo->Lgm_n_I_integrand_Calls, PostStr ); - printf("\t\t%sCurrent Dipole Moment, M_cd: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd, PostStr); - printf("\t\t%sReference Dipole Moment, M_cd_McIlwain: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd_McIlwain, PostStr); - printf("\t\t%sReference Dipole Moment, M_cd_2010: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd_2010, PostStr); - printf("\t\t%sDipole Moment Used, Mused: %g%s\n", PreStr, LstarInfo->Mused, PostStr); - printf("\t\t%sMcIlwain L (Hilton): %.15g%s\n", PreStr, L = LFromIBmM_Hilton( I, LstarInfo->mInfo->Bm, LstarInfo->Mused ), PostStr ); - printf("\t\t%sMcIlwain L (McIlwain): %.15g%s\n", PreStr, L = LFromIBmM_McIlwain( I, LstarInfo->mInfo->Bm, LstarInfo->Mused ), PostStr ); + printf("\t\t %sLgm_n_I_integrand_Calls: %d%s\n\n", PreStr, LstarInfo->mInfo->Lgm_n_I_integrand_Calls, PostStr ); + printf("\t\t%sCurrent Dipole Moment, M_cd: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd, PostStr); + printf("\t\t%sReference Dipole Moment, M_cd_McIlwain: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd_McIlwain, PostStr); + printf("\t\t%sReference Dipole Moment, M_cd_2010: %g%s\n", PreStr, LstarInfo->mInfo->c->M_cd_2010, PostStr); + printf("\t\t%sDipole Moment Used, Mused: %g%s\n", PreStr, LstarInfo->Mused, PostStr); + printf("\t\t%sMcIlwain L (Hilton): %.15g%s\n", PreStr, L = LFromIBmM_Hilton( I, LstarInfo->mInfo->Bm, LstarInfo->Mused ), PostStr ); + printf("\t\t%sMcIlwain L (McIlwain): %.15g%s\n", PreStr, L = LFromIBmM_McIlwain( I, LstarInfo->mInfo->Bm, LstarInfo->Mused ), PostStr ); } } else { @@ -822,7 +852,7 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ } else if ( LstarInfo->ISearchMethod == 2 ) { - // this method uses the mlat as the mlat of the footpoint. + // this method uses the mlat as the mlat of the footpoint. Lgm_Convert_Coords( &v2, &u, GSM_TO_SM, LstarInfo->mInfo->c ); } else { @@ -897,8 +927,9 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ * mlat. If this doesnt work, we expand the size of the range. * */ - done2 = FALSE; FoundShellLine = FALSE; Count = 0; + done2 = FALSE; FoundShellLine = FALSE; Count = 0; LstarInfo->nImI0 = 0; +//Iinitial = I; while ( !done2 && (k > 0) ) { if ( Count == 0 ) { @@ -1005,8 +1036,17 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ printf("\t\t%s________________________________________________________________________________________________________________________________%s\n", PreStr, PostStr ); } - FoundShellLine = FindShellLine( I, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, LstarInfo ); - if (FoundShellLine > 0) { +//printf( "LstarInfo->mInfo->Bm = %g\n", LstarInfo->mInfo->Bm ); +//printf( "MLT = %g\n", MLT ); +//printf( "mlat = %g\n", mlat ); +//printf( "r = %g\n", r ); +//printf( "mlat0 = %g\n", mlat0 ); +//printf( "mlat_try = %g\n", mlat_try ); +//printf( "mlat1 = %g\n", mlat1 ); +//printf( "nIts = %g\n", nIts ); + FoundShellLine = FindShellLine( I, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, 0, LstarInfo ); + if ( FoundShellLine > 0 ) { + // Found valid FL for drift shell done2 = TRUE; @@ -1033,24 +1073,43 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ // Check for multiple minima and bounce regions -- but only if we have a valid FL with correct (I,Bm) Type = ClassifyFL( k, LstarInfo ); - LstarInfo->nBounceRegions[k] = Type; if (LstarInfo->VerbosityLevel > 1) { printf("\t\t%sClassifying FL: Type = %d. %s\n", PreStr, Type, PostStr ); } - if ( (Type > 1) && (LstarInfo->ShabanskyHandling==LGM_SHABANSKY_HALVE_I) ){ + if ( (Type == 2) && (LstarInfo->ShabanskyHandling==LGM_SHABANSKY_HALVE_I) ){ + + /* + * This is likely a biffurcated drift orbit region. Re-do + * it by finding I0/2. Note that I/2 is not necessarily + * the correct partitioning. From debugging sessions, it + * seems that there are times when I/2 cannot be found + * exactly (but that for example I/2.1 could have been + * found.) These case tend to happne for multiple minima + * FLs. The error we may get back in this case is + * "Converged to something but not I". + */ + mlat0 -= 1.0; + mlat1 += 1.0; + if (LstarInfo->VerbosityLevel > 0) { - printf("\t\t\t%sShabansky orbit. Re-doing FL. Target I adjusted to: %g . (Original is: %g) %s\n", PreStr, I/2.0, I, PostStr ); } + printf("\t\t\t%sShabansky orbit. Re-doing FL. Target I adjusted to: %g . (Original is: %g) %s\n", PreStr, I/2.0, I, PostStr ); - FoundShellLine = FindShellLine( I/2.0, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, LstarInfo ); - //TODO: Do we need to test to make sure that the adjusted I is being found on a field line with multiple minima?? - PredMinusActualMlat = pred_mlat - mlat; - if (LstarInfo->VerbosityLevel > 1) { - printf("\t\t%s________________________________________________________________________________________________________________________________%s\n\n", PreStr, PostStr ); - printf("\t\t%s >> Pred/Actual/Diff mlat: %g/%g/%g MLT/MLAT: %g %g I0: %g I: %g I-I0/2: %g (SHABANSKY)%s\n", PreStr, pred_mlat, mlat, PredMinusActualMlat, MLT, mlat, I, Ifound, Ifound-I/2.0, PostStr ); - printf("\t\t%s________________________________________________________________________________________________________________________________ %s\n\n\n", PreStr, PostStr ); + FoundShellLine = FindShellLine( I/2.0, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, 1, LstarInfo ); + if ( FoundShellLine > 0 ) { // I0/2 worked + //TODO: Do we need to test to make sure that the adjusted I is being found on a field line with multiple minima?? + PredMinusActualMlat = pred_mlat - mlat; + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%s________________________________________________________________________________________________________________________________%s\n\n", PreStr, PostStr ); + printf("\t\t%s >> Pred/Actual/Diff mlat: %g/%g/%g MLT/MLAT: %g %g I0: %g I: %g I-I0/2: %g (SHABANSKY)%s\n", PreStr, pred_mlat, mlat, PredMinusActualMlat, MLT, mlat, I, Ifound, Ifound-I/2.0, PostStr ); + printf("\t\t%s________________________________________________________________________________________________________________________________ %s\n\n\n", PreStr, PostStr ); + } + } else { // redo old unpartitioned one (set RelaxTolerance to 2. This will force acceptance of anything that is a "converged but not to I value." +//printf("1. HERE\n"); + FoundShellLine = FindShellLine( I, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, 2, LstarInfo ); } + } else { if (Type > 1) { @@ -1065,6 +1124,65 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ } } + + } else if ( FoundShellLine == -4 ) { +done2 = TRUE; + // This is the "Converged to something, but not I error. It is possible that this is a shabansky and we should be searching for I0/2 (and that we coundt find any I0 vals in the search). + v = LstarInfo->mInfo->Pm_North; + LstarInfo->mInfo->Hmax = 0.1; + retEarthTrace = Lgm_TraceToSphericalEarth( &v, &w, LstarInfo->mInfo->Lgm_LossConeHeight, 1, 1e-9, LstarInfo->mInfo ); + if ( !retEarthTrace ){ + for ( i=0; inPnts; ++i ) if ( LstarInfo->nMinima[i] > 1 ) LstarInfo->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY; + break;//return(-4); + } + + + LstarInfo->Spherical_Footprint_Pn[k] = w; + LstarInfo->mInfo->Hmax = 0.05; + + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%sTracing Full FL so that we can classify it. Starting at Spherical_Footprint_Pn[%d] = %g %g %g%s\n", PreStr, k, LstarInfo->Spherical_Footprint_Pn[k].x, LstarInfo->Spherical_Footprint_Pn[k].y, LstarInfo->Spherical_Footprint_Pn[k].z, PostStr ); + } + Lgm_TraceLine( &LstarInfo->Spherical_Footprint_Pn[k], &v2, LstarInfo->mInfo->Lgm_LossConeHeight, -1.0, 1e-8, FALSE, LstarInfo->mInfo ); + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%sTraced Full FL so that we can classify it. Step size along FL: %g. Number of points: %d.%s\n", PreStr, LstarInfo->mInfo->Hmax, LstarInfo->mInfo->nPnts, PostStr ); + } + LstarInfo->Spherical_Footprint_Ps[k] = v2; + + // Check for multiple minima and bounce regions -- but only if we have a valid FL with correct (I,Bm) + Type = ClassifyFL( k, LstarInfo ); + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%sClassifying FL: Type = %d. %s\n", PreStr, Type, PostStr ); + } + + if ( (Type > 1) && (LstarInfo->ShabanskyHandling==LGM_SHABANSKY_HALVE_I) ){ +mlat0 -= 1.0; +mlat1 += 1.0; + if (LstarInfo->VerbosityLevel > 0) { + printf("\t\t\t%sShabansky orbit. Re-doing FL. Target I adjusted to: %g . (Original is: %g) %s\n", PreStr, I/2.0, I, PostStr ); +//printf("mlat0, mlat1 = %g %g\n", mlat0, mlat1); + } + + FoundShellLine = FindShellLine( I/2.0, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, 1, LstarInfo ); + if (FoundShellLine > 0) { +//printf("2a. HERE\n"); + //TODO: Do we need to test to make sure that the adjusted I is being found on a field line with multiple minima?? + PredMinusActualMlat = pred_mlat - mlat; + if (LstarInfo->VerbosityLevel > 1) { + printf("\t\t%s________________________________________________________________________________________________________________________________%s\n\n", PreStr, PostStr ); + printf("\t\t%s >> Pred/Actual/Diff mlat: %g/%g/%g MLT/MLAT: %g %g I0: %g I: %g I-I0/2: %g (SHABANSKY)%s\n", PreStr, pred_mlat, mlat, PredMinusActualMlat, MLT, mlat, I, Ifound, Ifound-I/2.0, PostStr ); + printf("\t\t%s________________________________________________________________________________________________________________________________ %s\n\n\n", PreStr, PostStr ); + } + + } else { // redo old unpartitioned one (set RelaxTolerance to 2. This will force acceptance of anything that is a "converged but not to I value." +//printf("2b. HERE\n"); + FoundShellLine = FindShellLine( I, &Ifound, LstarInfo->mInfo->Bm, MLT, &mlat, &r, mlat0, mlat_try, mlat1, &nIts, 2, LstarInfo ); + } + + } + + + } else if ( Count > 2 ) { // Tried to find valid FL more than three times done2 = TRUE; @@ -1076,6 +1194,9 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ } } //end of while loop + + + /* * Test for open drift path and exit gracefully if required */ @@ -1086,7 +1207,9 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ // After initial FL, found I,Bm in bifurcated drift orbit. Desired behavior is bailing out. doExit = TRUE; } - if (doExit) { + if ( doExit ) { + + for ( i=0; inPnts; ++i ) { if ( LstarInfo->nMinima[i] > 1 ) { if ( LstarInfo->nBounceRegions[i] > 1 ) { @@ -1097,9 +1220,11 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ } } - if (nShabII > 0) { + + + if ( nShabII > 0 ) { LstarInfo->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY_II; - } else if (nShabI > 0) { + } else if ( nShabI > 0 ) { LstarInfo->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY_I; } else { LstarInfo->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN; @@ -1206,12 +1331,15 @@ int Lstar( Lgm_Vector *vin, Lgm_LstarInfo *LstarInfo ){ * footpoint, we start there and trace to south. So lets pack them in * the saved arrays backwards so that they go from south to north. */ +// see what it is first +LstarInfo->Pmin[k] = LstarInfo->mInfo->Pmin; LstarInfo->FindShellPmin = TRUE; +//But this is probably not the Pmin for Shabnsky regions if ( LstarInfo->FindShellPmin || LstarInfo->ComputeVgc ) { //Lgm_TraceToMinBSurf( &LstarInfo->Spherical_Footprint_Pn[k], &v2, 0.1, 1e-8, LstarInfo->mInfo ); Lgm_TraceToMinBSurf( &LstarInfo->Spherical_Footprint_Pn[k], &v2, 0.1, 1e-8, LstarInfo->mInfo ); LstarInfo->mInfo->Bfield( &v2, &LstarInfo->Bmin[k], LstarInfo->mInfo ); - LstarInfo->Pmin[k] = v2; +//LstarInfo->Pmin[k] = v2; } /* @@ -1550,22 +1678,29 @@ double MagFlux( Lgm_LstarInfo *LstarInfo ) { double LambdaIntegrand( double Lambda, _qpInfo *qpInfo ) { - double cl, sl, st, ct, sp, cp, Br, Bx, By, Bz, f; + double r, cl, sl, st, ct, sp, cp, Br, Bx, By, Bz, f; double MLT, phi; Lgm_Vector u, w, Bvec; Lgm_LstarInfo *LstarInfo; + + /* * Get pointer to our auxilliary data structure. */ LstarInfo = (Lgm_LstarInfo *)qpInfo; + // Must calculate the field at the right altitude. This should be the same + // altitude (above a *spherical* Earth) that the mlat intersection of the + // drift shell was computed for. + r = 1.0 + LstarInfo->mInfo->Lgm_LossConeHeight/WGS84_A; + MLT = LstarInfo->Phi*DegPerRad/15.0; phi = 15.0*(MLT-12.0)*RadPerDeg; cl = cos( Lambda ); sl = sin( Lambda ); - u.x = cl*cos( phi ); - u.y = cl*sin( phi ); - u.z = sl; + u.x = r*cl*cos( phi ); + u.y = r*cl*sin( phi ); + u.z = r*sl; Lgm_Convert_Coords( &u, &w, SM_TO_GSM, LstarInfo->mInfo->c ); @@ -1576,7 +1711,7 @@ double LambdaIntegrand( double Lambda, _qpInfo *qpInfo ) { st = sin( M_PI/2.0 - Lambda ); ct = cos( M_PI/2.0 - Lambda ); sp = sin( phi ); cp = cos( phi ); Br = st*cp*Bx + st*sp*By + ct*Bz; - f = Br*cos( Lambda ); + f = Br*r*r*cos( Lambda ); return( f ); @@ -1712,9 +1847,10 @@ double MagFlux2( Lgm_LstarInfo *LstarInfo ) { */ - r = 1.0 + LstarInfo->mInfo->Lgm_LossConeHeight/WGS84_A; +// r = 1.0 + LstarInfo->mInfo->Lgm_LossConeHeight/WGS84_A; +// return( result/r ); - return( result/r ); + return( result ); } diff --git a/libLanlGeoMag/DriftShell.c b/libLanlGeoMag/DriftShell.c index bd9554285..031bd3d73 100644 --- a/libLanlGeoMag/DriftShell.c +++ b/libLanlGeoMag/DriftShell.c @@ -4,10 +4,15 @@ #include #define MAX_ITS 100 + + +#define TRACE_TOL 1e-7 + typedef struct BracketType { double a, b, c; double Ia, Ib, Ic; double Da, Db, Dc; + int Erra, Errb, Errc; double mlat_min, Dmin; int FoundZeroBracket; } BracketType; @@ -43,25 +48,28 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * \param[in,out] mlat an initial guess for the magnetic latitude of the mirror pointat which. * \param[in,out] rad the geocentric radial distance of the mirror point (at which B=Bm). * \param[in] mlat0 lower end of mlat bracket to search over. - * \param[in] mlat1 upper end of mlat bracket to search over. + * \param[in] mlat1 best guess for what mlat should be. + * \param[in] mlat2 upper end of mlat bracket to search over. * \param[in,out] LstarInfo A properly initialized Lgm_LstarInfo structure. * * \return 1 - normal exit. Field line found with acceptable accuracy. * \return 0 - no field line could be found * */ -int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *mlat, double *rad, double mlat0, double mlat1, double mlat2, int *Iterations, Lgm_LstarInfo *LstarInfo) { +int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *mlat, double *rad, double mlat0, double mlat1, double mlat2, int *Iterations, int RelaxTolerance, Lgm_LstarInfo *LstarInfo) { Lgm_Vector u, w, Pm_North, Pmirror, v1, v2, v3; - double F, F0, F1, rat, a, b, c, d, d0, d1, Da, Db, Dc, De, I, r, Phi, cl, sl; + double F, F0, F1, rat, a, b, c, d, d0, d1, Da, Db, Dc, De, ff, I, r, Phi, cl, sl; double SS, Sn, Ss, mlat_min=0.0, Dmin=9e99, e, D, D0, D1, D2, Sign, Dbest, mlatbest, res; int done, FirstHalf, nIts, FoundZeroBracket; int i, Flag, nbFits; BracketType Bracket; double BRACKET_EPS; + int ErrorStatus; int FoundValidI = 0; *Iterations = 0; +//printf("FindShellLine: I0 = %g\n", I0); /* * Set the bracket for the mlat range. We got mlat0 and mlat2 from the @@ -100,6 +108,7 @@ int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *ml * d) Switch to bisection root finder using the new bracketed * zero. * + * */ Dmin = 9e99; @@ -117,6 +126,12 @@ int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *ml * Attempt to bracket the zero in I-I0. Note that this may not be easy if * the curve doesnt drop very substantially below the zero point. This is * often the case for large eq. pitch angles. + * + * A related problem (worse for large pitch angle) is this: We may actually + * be close to the FL that we are looking for, but in order to compute I, + * we must have Bmin < Bmirror. If this is not the case, we wont even get + * an I. We need to be careful of ths! + * */ Bracket.Dmin = 9e99; Flag = BracketZero( I0, Ifound, Bm, MLT, mlat, rad, mlat0, mlat1, mlat2, &Bracket, LstarInfo ); @@ -224,7 +239,8 @@ mlatbest = mlat_min; b = res; c = res+2.0; - I = ComputeI_FromMltMlat( Bm, MLT, b, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, b, &r, I0, &ErrorStatus, LstarInfo ); +//if I is valid if ( fabs(I) < 1e98 ) { D0 = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = b; @@ -267,7 +283,8 @@ mlatbest = mlat_min; if ( !FoundZeroBracket ) { - I = ComputeI_FromMltMlat( Bm, MLT, a, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, a, &r, I0, &ErrorStatus, LstarInfo ); +//if I is valid if ( fabs(I) < 1e98 ) { LstarInfo->MLATarr[LstarInfo->nImI0] = a; LstarInfo->ImI0arr[LstarInfo->nImI0++] = I-I0; @@ -307,7 +324,8 @@ mlatbest = mlat_min; if ( !FoundZeroBracket ) { - I = ComputeI_FromMltMlat( Bm, MLT, c, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, c, &r, I0, &ErrorStatus, LstarInfo ); +//if I is valid if ( fabs(I) < 1e98 ) { LstarInfo->MLATarr[LstarInfo->nImI0] = c; LstarInfo->ImI0arr[LstarInfo->nImI0++] = I-I0; @@ -415,7 +433,8 @@ mlatbest = mlat_min; FirstHalf = FALSE; e = b + F1*d1; } - I = ComputeI_FromMltMlat( Bm, MLT, e, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, e, &r, I0, &ErrorStatus, LstarInfo ); +//return value ignored? LstarInfo->MLATarr[LstarInfo->nImI0] = e; LstarInfo->ImI0arr[LstarInfo->nImI0++] = I-I0; De = I-I0; @@ -478,10 +497,22 @@ mlatbest = mlat_min; * bracket. */ done = TRUE; - if ( fabs(De) < 0.001 ) { + ff = fabs(De)/I; + if ( RelaxTolerance && ff < 0.3 ) { + /* + * The RelaxTolerance flag is set (probably trying to do a + * I/2 partioning due to Shabansky detection.) Lets not get + * too greedy, since I/2 isnt likely totally correct + * anyway! + */ + FoundValidI = TRUE; // lets just take what we get here.... + *mlat = mlat_min; // Use the best value we got + } else if ( !RelaxTolerance && ff < 0.05 ) { FoundValidI = TRUE; // lets just take what we get here.... *mlat = mlat_min; // Use the best value we got } else { +//printf("HERE\n"); + *mlat = mlat_min; FoundValidI = -4; } } else if ( De < Db ) { @@ -584,7 +615,8 @@ mlatbest = mlat_min; } - I = ComputeI_FromMltMlat( Bm, MLT, e, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, e, &r, I0, &ErrorStatus, LstarInfo ); +//return value ignored? LstarInfo->MLATarr[LstarInfo->nImI0] = e; LstarInfo->ImI0arr[LstarInfo->nImI0++] = I-I0; De = I-I0; @@ -642,15 +674,41 @@ mlatbest = mlat_min; * bracket. */ done = TRUE; - if ( fabs(De) < 0.001 ) { + ff = fabs(De)/I; + if ( RelaxTolerance == 2 ) { + FoundValidI = TRUE; // lets just take what we get here.... + *mlat = mlat_min; // Use the best value we got + if (LstarInfo->VerbosityLevel > 1){ + printf( "\t\t\t> Converged to something (but not I?): fabs(a-b) = %g, but (I-I0) = %g |I-I0|/I = %g I GIVE UP. ACCEPTING RESULT ANYWAY (Prob have the right mlat, but wrong I0?) (Relaxed Tolerance = 2)\n", fabs(a-b), De, fabs(De)/I0 ); + printf( "\t\t\t> Bracket: a, b, c = %g %g %g Da, Db, Dc = %g %g %g\n", a, b, c, Da, Db, Dc ); + } + } else if ( (RelaxTolerance == 1) && (ff < 0.3) ) { + /* + * The RelaxTolerance flag is set (probably trying to do a + * I/2 partioning due to Shabansky detection.) Lets not get + * too greedy, since I/2 isnt likely totally correct + * anyway! + */ FoundValidI = TRUE; // lets just take what we get here.... *mlat = mlat_min; // Use the best value we got + if (LstarInfo->VerbosityLevel > 1){ + printf( "\t\t\t> Converged to something (but not I?): fabs(a-b) = %g, but (I-I0) = %g |I-I0|/I = %g ACCEPTING RESULT since |I-I0|/I < 0.1 (Relaxed Tolerance)\n", fabs(a-b), De, fabs(De)/I0 ); + printf( "\t\t\t> Bracket: a, b, c = %g %g %g Da, Db, Dc = %g %g %g\n", a, b, c, Da, Db, Dc ); + } + } else if ( (RelaxTolerance == 0) && (ff < 0.05) ) { + FoundValidI = TRUE; // lets just take what we get here.... + *mlat = mlat_min; // Use the best value we got + if (LstarInfo->VerbosityLevel > 1){ + printf( "\t\t\t> Converged to something (but not I?): fabs(a-b) = %g, but (I-I0) = %g |I-I0|/I = %g ACCEPTING RESULT since |I-I0|/I < 0.01\n", fabs(a-b), De, fabs(De)/I0 ); + printf( "\t\t\t> Bracket: a, b, c = %g %g %g Da, Db, Dc = %g %g %g\n", a, b, c, Da, Db, Dc ); + } } else { + *mlat = mlat_min; FoundValidI = -4; - } - if (LstarInfo->VerbosityLevel > 1){ - printf( "\t\t\t> Converged to something (but not I?): fabs(a-b) = %g, but |I-I0| = %g\n", fabs(a-b), De ); - printf( "\t\t\t> Bracket: a, b, c = %g %g %g Da, Db, Dc = %g %g %g\n", a, b, c, Da, Db, Dc ); + if (LstarInfo->VerbosityLevel > 1){ + printf( "\t\t\t> Converged to something (but not I?): fabs(a-b) = %g, but (I-I0) = %g |I-I0|/I = %g \n", fabs(a-b), De, fabs(De)/I0 ); + printf( "\t\t\t> Bracket: a, b, c = %g %g %g Da, Db, Dc = %g %g %g\n", a, b, c, Da, Db, Dc ); + } } } else if ( De > 0.0 ) { if ( Da > 0.0 ) { @@ -1158,7 +1216,7 @@ int FitQuadAndFindZero2( double *xin, double *yin, double *dyin, int n, int nmax f[i].y = yin[i]; f[i].dy = dyin[i]; f[i].key = fabs(yin[i]); - //printf("|y|, y, x, = %g %g %g\n", fabs(yin[i]), yin[i], xin[i] ); + printf("|y|, y, x, = %g %g %g\n", fabs(yin[i]), yin[i], xin[i] ); } elt_qsort( f, n ); @@ -1233,18 +1291,46 @@ int FitQuadAndFindZero2( double *xin, double *yin, double *dyin, int n, int nmax } +double ComputeBmin( double MLT, double mlat, Lgm_LstarInfo *LstarInfo ){ + double rr, Phi, SinPhi, CosPhi, cl, sl, Bmin; + Lgm_Vector Bvec, v1, v2, v3, u, w; + int TraceFlag; + + + rr = 1.0 + 100.0/Re; + Phi = 15.0*(MLT-12.0)*RadPerDeg; + SinPhi = sin(Phi); CosPhi = cos(Phi); + + cl = cos( mlat * RadPerDeg ); sl = sin( mlat * RadPerDeg ); + w.x = rr*cl*CosPhi; w.y = rr*cl*SinPhi; w.z = rr*sl; + Lgm_Convert_Coords( &w, &u, SM_TO_GSM, LstarInfo->mInfo->c ); + TraceFlag = Lgm_Trace( &u, &v1, &v2, &v3, LstarInfo->mInfo->Lgm_LossConeHeight, TRACE_TOL, TRACE_TOL, LstarInfo->mInfo ); +//printf("v3 = %g %g %g\n", v3.x, v3.y, v3.z); + LstarInfo->mInfo->Bfield( &v3, &Bvec, LstarInfo->mInfo ); + Bmin = Lgm_Magnitude( &Bvec ); + + return( Bmin ); + +} /* * This routine is designed to find a bracket on the D=0 root. Here, D = I-I0. * A zero bracket is defined by having D change signs -- then there must (or * probably) is a root in between. + * + * This is not as trivial as you would think... + * + * */ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, double *rad, double mlat0, double mlat1, double mlat2, BracketType *Bracket, Lgm_LstarInfo *LstarInfo ){ double I, r, D, D0, D1, D2, Dmin, mlat_min, mmm; int FoundZeroBracket, nDefined, done; + int ErrorStatus; + double mlat_a, mlat_b, mlat_t; + double dB_a, dB_b, dB_t; BracketType Bracket0; Dmin = Bracket->Dmin; @@ -1252,6 +1338,143 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, nDefined = 0; +//printf("mlat0, mlat1, mlat2: %g %g %g\n", mlat0, mlat1, mlat2); + + /* + * Let us first check that mlat2 > mlat1 > mlat0 + */ + if ( mlat0 > mlat1 ) { return( -1 ); } + if ( mlat0 > mlat2 ) { return( -1 ); } + if ( mlat1 > mlat2 ) { return( -1 ); } + + + + /* + * A major problem will occur if our bracket has FLs inside that yield Bmin + * > Bmirror. Lets, elaborate on this.... + * + * Lets say we are looking for a small I value that results from a high + * pitch angle (e.g. 86.4deg.) + * + * Then, it is quite possible to find a FL that has a Bmin value that is + * greater then the Bmirror values we are trying to find. This is a + * problem because it will never work -- except possibly if you are in the + * local high-point of a shabansky-type FL. But forget than for now. + * We have to protect our bracket from having these. + * + * ComputeI_FromMltMlat() will return -10 in the ErrorStatus when Bmin > + * Bmirror. So, if we get -10 at the endpoints thats bad -- we need to + * adjust until we dont. But we also dont want to miss out on a region of + * potential solutions. In other words, we need to go slow. + * + * Note that in a reasonable world (magnetosphere?), getting the dreaded + * -10 error means we are not high enough in latitude to get Bmin's that + * are small enough. But lets not be silly about it and just keep + * computing I's. All we need is the mlat where Bmin <= Bmirror in order + * to get a valid endpoint for our potential bracket. And we can do that + * as a simple search usingm Lgm_TraceToBmin() + * + */ + + + /* + * This is for ISearchMethod = 2 + * This section assumes the mlats are the footpoints. + */ + if (LstarInfo->ISearchMethod == 2){ + if (LstarInfo->VerbosityLevel > 2){ + printf("mlat0, mlat1, mlat2: %g %g %g\n", mlat0, mlat1, mlat2); + } + /* + * Check the 3 mlat's to ensure we dont get -10. + * If we get a -10, it means that the B_mirror value we are looking + * for cant possible be on the FL since the Bmin of the FL is Greater + * than what we are looking for... + */ + mlat_a = mlat0; dB_a = Bm - ComputeBmin( MLT, mlat_a, LstarInfo ); + if (LstarInfo->VerbosityLevel > 2){ + printf("mlat_a, Bm, Bmin, dB_a = %g %g %g %g\n", mlat_a, Bm, Bm-dB_a, dB_a ); + } + if ( dB_a < 0.0 ) { + + // Set starting point to lower value + mlat_t = mlat_a; dB_t = dB_a; + + done = FALSE; + while ( !done ) { + + // Step up until we cross zero + mlat_t += 0.1; dB_t = Bm - ComputeBmin( MLT, mlat_t, LstarInfo ); + if (LstarInfo->VerbosityLevel > 2){ + printf("mlat_t, dB_t = %g %g\n", mlat_t, dB_t ); + } + + if ( dB_t < 0.0 ) { + // Havent found the crossing yet -- advance the lower value + mlat_a = mlat_t; dB_a = dB_t; + } else if ( dB_t >= 0.0 ) { + // Found the crossing! We have a bracket on zero for dB. -- set upper bracket + done = TRUE; + mlat_b = mlat_t; dB_b = dB_t; + } + + } + + /* + * Now use bisection + */ + if (LstarInfo->VerbosityLevel > 2) printf("Switching to bisection\n"); + done = FALSE; + while ( !done ) { + + //Try halfway point. + mlat_t = 0.5*(mlat_a+mlat_b); dB_t = Bm - ComputeBmin( MLT, mlat_t, LstarInfo ); + if (LstarInfo->VerbosityLevel > 2) { + printf("mlat_t, dB_t = %g %g\n", mlat_t, dB_t ); + } + + if ( dB_t < 0.0 ) { + // Still negative -- advance the lower value + mlat_a = mlat_t; dB_a = dB_t; + } else { + // positive -- advance the upper value + mlat_b = mlat_t; dB_b = dB_t; + } + + if ( fabs( mlat_b - mlat_a ) < 1e-5 ) { + done = TRUE; + } + + } + + // Use the one on the positive side + mlat0 = mlat_b; + } + + + // We may have gone beyond the other points -- check and correct + if ( ( mlat0 >= mlat1 ) && ( mlat0 < mlat2 ) ) { + // We've exceeded the center point but not the upper point. + mlat1 = 0.5*(mlat0 + mlat2); + } + if ( ( mlat0 >= mlat1 ) && ( mlat0 >= mlat2 ) ) { + // We've exceeded the center point but not the upper point. + mlat2 = mlat0 + 5.0; // ???? reasoanble ???? + mlat1 = 0.5*(mlat0 + mlat2); + } + + if (LstarInfo->VerbosityLevel > 2) { + printf("mlat0, mlat1, mlat2: %g %g %g\n", mlat0, mlat1, mlat2); + } +} + +//exit(0); + + + + + + /* @@ -1263,8 +1486,12 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * I(mlat1) * */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat1, &r, I0, LstarInfo ); +//printf("Bm, MLT, mlat1, r = %g %g %g %g I=%g I0 = %g\n", Bm, MLT, mlat1, r, I, I0); + I = ComputeI_FromMltMlat( Bm, MLT, mlat1, &r, I0, &ErrorStatus, LstarInfo ); +//printf("Bm, MLT, mlat1, r = %g %g %g %g I=%g I0 = %g\n", Bm, MLT, mlat1, r, I, I0); Bracket->b = mlat1; Bracket->Ib = (I < 1e6) ? I : -1e31; Bracket->Db = Bracket->Ib - I0; + Bracket->Errb = ErrorStatus; + //printf("ErrorStatus = %d\n", ErrorStatus ); if ( Bracket->Ib > 0.0 ) { // Cache I(mlat) point for possible future fitting. LstarInfo->MLATarr[LstarInfo->nImI0] = Bracket->b; @@ -1293,8 +1520,11 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * I(mlat0) * */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, &ErrorStatus, LstarInfo ); Bracket->a = mlat0; Bracket->Ia = (I < 1e6) ? I : -1e31; Bracket->Da = Bracket->Ia - I0; + Bracket->Erra = ErrorStatus; + //printf("ErrorStatus = %d\n", ErrorStatus ); + if ( Bracket->Ia > 0.0 ) { LstarInfo->MLATarr[LstarInfo->nImI0] = Bracket->a; LstarInfo->ImI0arr[LstarInfo->nImI0++] = Bracket->Da; @@ -1337,8 +1567,10 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * I(mlat2) * */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, &ErrorStatus, LstarInfo ); Bracket->c = mlat2; Bracket->Ic = (I < 1e6) ? I : -1e31; Bracket->Dc = Bracket->Ic - I0; + Bracket->Errc = ErrorStatus; + //printf("ErrorStatus = %d\n", ErrorStatus ); if ( Bracket->Ic > 0.0 ) { LstarInfo->MLATarr[LstarInfo->nImI0] = Bracket->c; LstarInfo->ImI0arr[LstarInfo->nImI0++] = Bracket->Dc; @@ -1357,9 +1589,11 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, } - //printf("\n\t\t\t> BracketZero: mlat0 = %g I-I0 = %g\n", Bracket->a, Bracket->Da ); - //printf("\t\t\t> BracketZero: mlat1 = %g I-I0 = %g\n", Bracket->b, Bracket->Db ); - //printf("\t\t\t> BracketZero: mlat2 = %g I-I0 = %g\n\n", Bracket->c, Bracket->Dc ); + if (LstarInfo->VerbosityLevel > 1){ + printf("\n\t\t\t> BracketZero: mlat0 = %g I-I0 = %g\n", Bracket->a, Bracket->Da ); + printf("\t\t\t> BracketZero: mlat1 = %g I-I0 = %g\n", Bracket->b, Bracket->Db ); + printf("\t\t\t> BracketZero: mlat2 = %g I-I0 = %g\n\n", Bracket->c, Bracket->Dc ); + } /* * We already checked the [a,b] pair for a zero bracket above. @@ -1501,7 +1735,8 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, while (!done) { mmm = (Bracket->a + Bracket->b)/2.0; - I = ComputeI_FromMltMlat( Bm, MLT, mmm, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mmm, &r, I0, &ErrorStatus, LstarInfo ); +//if Ib is valid if ( I < 1e6 ) { D = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = mmm; @@ -1570,7 +1805,7 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * Compute I-I0 at best-guess mlat. If the caller predicted well, then this * may be dead on, so try it first. */ -//I = ComputeI_FromMltMlat( Bm, MLT, mlat1, &r, I0, LstarInfo ); +//I = ComputeI_FromMltMlat( Bm, MLT, mlat1, &r, I0, &ErrorStatus, LstarInfo ); //if ( fabs(I) > 1e99 ) { // printf("\t\t\t> BracketZero: I undefined at mlat1 = %g\n", mlat1 ); // return(-5); @@ -1615,7 +1850,8 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * I.e. we need a smaller I which (usually) is a smaller mlat. So try * mlat0 next. */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, &ErrorStatus, LstarInfo ); +//if I is bad if ( fabs(I) > 1e99 ) return(-5); D0 = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = mlat0; @@ -1650,7 +1886,8 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * * Still no bracket. Evaluate the other side. */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, &ErrorStatus, LstarInfo ); +// if I is bad if ( fabs(I) > 1e99 ) return(-5); D2 = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = mlat2; @@ -1695,7 +1932,8 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, * Then we would like to have the other side of the bracket be > 0.0. * I.e. we need a bigger I which (usually) is a bigger mlat. */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat2, &r, I0, &ErrorStatus, LstarInfo ); +// if I is bad if ( fabs(I) > 1e99 ) return(-5); D2 = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = mlat2; @@ -1728,7 +1966,8 @@ int BracketZero( double I0, double *Ifound, double Bm, double MLT, double *mlat, /* * Still no bracket. Evaluate the other side. */ - I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, LstarInfo ); + I = ComputeI_FromMltMlat( Bm, MLT, mlat0, &r, I0, &ErrorStatus, LstarInfo ); +// if I is bad if ( fabs(I) > 1e99 ) return(-5); D0 = I-I0; LstarInfo->MLATarr[LstarInfo->nImI0] = mlat0; diff --git a/libLanlGeoMag/FluxTubeVolume.c b/libLanlGeoMag/FluxTubeVolume.c new file mode 100644 index 000000000..b5da2d476 --- /dev/null +++ b/libLanlGeoMag/FluxTubeVolume.c @@ -0,0 +1,154 @@ +#include "Lgm/Lgm_MagModelInfo.h" +//#include "MagStep.h" + +#define JUMP_METHOD 0 + +#define USE_SIX_POINT 0 +#define USE_FOUR_POINT 1 +#define USE_TWO_POINT 2 + +#define DIFF_SCHEME USE_TWO_POINT +//#define DIFF_SCHEME USE_FOUR_POINT + +/* + * Computes the following integral + * + * + * / north_footpoint + * | + * | [ 1 ] + * V = | [ -------- ] ds + * | [ B(s) ] + * | + * / south_footpoint + * + * + * + */ + + + + + +/** + * This routine evaluates the "flux tube volume, V" from footpoint to footpoint + * + * The integral is as follows: + * + * \f[ + * V = \int_{sf_{south}}^{sf_{north}} + * \left\{ 1\over B(s) \right\} ds + * \f] + * + * + * + * + * \param[in,out] mInfo A properly initialized Lgm_MagModelInfo structure. + * + * \return V, The flux tube volume. + * + * + * \note + * - The routine needs the following values set properly in the mInfo structure; + * - mInfo->Stotal + * - mInfo->Lgm_V_Integrator_epsabs + * - mInfo->Lgm_V_Integrator_epsrel + * - mInfo->Lgm_V_Integrator + * - other things too (model info etc...) + * \note + * - On exit, the following will be set; + * - other things... + * + * + */ +double FluxTubeVolume( Lgm_MagModelInfo *mInfo ) { + + double a, b; + double epsabs, epsrel, result, abserr, resabs, resasc; + int key, limit, lenw; + int iwork[501], last, ier, neval; + double work[2002]; + _qpInfo *qpInfo; + + + /* + * Type-cast our data structure to a generic type. + * The structure holds auzilliary info we need down + * in the function calls. + */ + qpInfo = (_qpInfo *)mInfo; + + /* + * Limits of integration. + */ + a = 0.0; // Southern footpoint + b = mInfo->Stotal; // Northern footpoint + + + /* + * set tolerances for QuadPack routines. + */ + //epsabs = mInfo->epsabs; + //epsrel = mInfo->epsrel; + epsabs = mInfo->Lgm_V_Integrator_epsabs; + epsrel = mInfo->Lgm_V_Integrator_epsrel; + + + /* + * Init some vars used in V_integrand() (these are not declared static in + * V_integrand() in order to avoid making it non-reentrant). + */ + mInfo->Lgm_n_V_integrand_Calls = 0; + + + + if ( mInfo->Lgm_V_Integrator == DQAGS ) { + + /* + * Use DQAGS + */ + limit = 500; lenw = 4*limit; key = 6; + dqags(V_integrand, qpInfo, a, b, epsabs, epsrel, &result, &abserr, &neval, &ier, limit, lenw, &last, iwork, work, mInfo->VerbosityLevel ); + + } else if ( mInfo->Lgm_V_Integrator == DQK21 ) { + + /* + * Use DQK21 + */ + dqk21(V_integrand, qpInfo, a, b, &result, &abserr, &resabs, &resasc); + + } else { + + /* + * Unknown Integrator + */ + printf("FluxTubeVolume: Unknown integrator. Lgm_V_Integrator = %d\n", mInfo->Lgm_n_V_integrand_Calls ); + result = LGM_FILL_VALUE; + + } + + return( result ); + +} + + +double V_integrand( double s, _qpInfo *qpInfo ) { + + double B, g, f; + Lgm_MagModelInfo *mInfo; + + /* + * Get pointer to our auxilliary data structure. + */ + mInfo = (Lgm_MagModelInfo *)qpInfo; + + + B = BofS( s, mInfo ); + + f = 1.0/B; + + ++mInfo->Lgm_n_V_integrand_Calls; + + return(f); + +} diff --git a/libLanlGeoMag/GPR.c b/libLanlGeoMag/GPR.c new file mode 100644 index 000000000..47e0786a5 --- /dev/null +++ b/libLanlGeoMag/GPR.c @@ -0,0 +1,1090 @@ +#include +#include +#include +#include +#include +#include +#include + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZeroNegativeEigenvalues 1 +#define ReverseNegativeEigenvalues 0 + +typedef struct GprInfo { + + double sigma_f, l; + double log_sigma_f, log_l; + + gsl_vector *x; + gsl_vector *y; + gsl_vector *sigma_n_vec; + gsl_vector *sigma_n_2_vec; + double sigma_n; + double sigma_n_2; + + gsl_matrix *K; + gsl_matrix *K_dl; + gsl_matrix *K_dsig; + gsl_matrix *K_inv; + gsl_vector *r; + gsl_matrix *A; + gsl_matrix *C; + + //double logp, dlogp_dsig, dlogp_dl; + double logp, dlogp_dlog_sig, dlogp_dlog_l; + + + /* + * These hold the final mean and credibility intervals + */ + gsl_vector *y_hat; + gsl_vector *y_cred_lo; + gsl_vector *y_cred_hi; + +} GprInfo; + + + + + + + + +unsigned char Ctab_Red[] = { 0, 76, 78, 79, 80, 81, 83, 84, 85, 86, 88, 89, 90, 92, 93, 94, 95, 97, 98, 99, 100, 102, 103, 104, 106, 107, 108, 109, 111, 112, 113, 114, 116, 117, 118, 119, 121, 122, 123, 125, 126, 127, 128, 130, 131, 132, 133, 135, 136, 137, 139, 140, 141, 142, 144, 145, 146, 147, 149, 150, 151, 152, 154, 155, 156, 158, 159, 160, 161, 163, 164, 165, 166, 168, 169, 170, 172, 173, 174, 175, 177, 178, 179, 180, 182, 183, 184, 186, 187, 188, 189, 191, 192, 193, 194, 196, 197, 198, 199, 201, 202, 203, 205, 206, 207, 208, 210, 211, 212, 213, 215, 216, 217, 219, 220, 221, 222, 224, 225, 226, 227, 229, 230, 231, 232, 234, 235, 236, 238, 238, 238, 238, 238, 238, 238, 239, 239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, 241, 241, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 249, 249, 249, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 252, 251, 250, 250, 249, 248, 247, 246, 245, 245, 244, 243, 242, 241, 240, 240, 239, 238, 237 }; +unsigned char Ctab_Grn[] = { 0, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 29, 29, 30, 30, 31, 32, 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39, 40, 40, 41, 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 59, 60, 60, 61, 62, 63, 64, 64, 65, 66, 67, 68, 69, 69, 70, 71, 72, 73, 74, 75, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 132, 133, 134, 135, 136, 137, 139, 140, 141, 142, 143, 144, 146, 147, 148, 149, 150, 151, 153, 154, 155, 156, 157, 158, 160, 161, 162, 163, 164, 165, 167, 168, 169, 170, 171, 172, 174, 175, 176, 177, 178, 179, 181, 182, 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206, 207, 208, 209, 211, 212, 213, 214, 215, 216, 217, 219, 220, 221, 222, 223, 224, 225, 227, 228, 229, 230, 231, 232, 233, 235, 236, 237, 238, 239, 240, 241, 242, 244, 245, 246, 247, 248, 249, 250, 251, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }; +unsigned char Ctab_Blu[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25 }; +/** + * Routine to write out a GIF image + */ +void DumpGif3( char *FilenameBase, int W, int H, double **Image ){ + + double Val, Val2, Min, Max, dVal; + int w, h; + char Filename[1024]; + unsigned char *uImage, uVal; + FILE *fp_gif, *fp_info; + + int LogScale; + + LogScale = TRUE; + LogScale = FALSE; + + + // Determine Min/Max values... + Min = 9e99; + Max = -9e99; + for ( w=0; w 0.0) ? log10( Val ) : -9e99; + } else { + Val2 = Image[h][w]; + } + if (Val2 > Max) Max = Val2; + if (Val2 < Min) Min = Val2; + //if ((Val2 < Min)&&(Val > 0.0)) Min = Val2; + + } + } + + printf("Min, Max = %g %g\n", Min, Max); +//Min = 1.9; +//Max = 3.5; + +/* +Min = -3.0; +Max = 1.0; +*/ + + sprintf( Filename, "%s.info", FilenameBase); + fp_info = fopen( Filename, "w" ); + fprintf( fp_info, "Min: %g\n", Min ); + fprintf( fp_info, "Max: %g\n", Max ); + fclose( fp_info ); + + + + uImage = (unsigned char *)calloc( W*H, sizeof(unsigned char) ); + + for ( w=0; w 0.0 ? log10( Image[h][w] ) : -1e31; + } else { + Val = Image[h][w]; + } + if ( Val < Min ) { + uVal = 0; + } else { + dVal = (Val - Min)/(Max-Min)*255.0; + uVal = (dVal > 255.0) ? 255 : (unsigned char)dVal; + } + + *(uImage + W*(h) + W-1-w) = uVal; + + } + } + + sprintf( Filename, "%s.gif", FilenameBase); + fp_gif = fopen(Filename, "w"); + WriteGIF(fp_gif, (byte *)uImage, 0, W, H, Ctab_Red, Ctab_Grn, Ctab_Blu, 256, 0, ""); + fclose(fp_gif); + + free( uImage ); + + + + // dump a colorbar image +if (0==1){ + W = 10; H = 256; + uImage = (unsigned char *)calloc( W*H, sizeof(unsigned char) ); + for ( w=0; wx = gsl_vector_calloc( n ); + Info->y = gsl_vector_calloc( n ); + Info->sigma_n_vec = gsl_vector_calloc( n ); + Info->sigma_n_2_vec = gsl_vector_calloc( n ); + + Info->K = gsl_matrix_calloc( n, n ); + Info->K_dl = gsl_matrix_calloc( n, n ); + Info->K_dsig = gsl_matrix_calloc( n, n ); + Info->K_inv = gsl_matrix_calloc( n, n ); + Info->r = gsl_vector_calloc( n ); + Info->A = gsl_matrix_calloc( n, n ); + Info->C = gsl_matrix_calloc( n, n ); + +} + +/* + * This is written up in Gaussian Processes for Machine Learning Carl Edward + * Rasmussen and Christopher K. I. Williams MIT Press, 2006. ISBN-10 + * 0-262-18253-X, ISBN-13 978-0-262-18253-9. + * + * See equation 5.8. Also see equation 2.30 and the appnedix for some math + * machinery. + * + * From eqn 5.8, the so-called "marginal liklehood" or "evidence" is given by: + * + * log ( p(y|X,Theta) ) = -1/2 y^T K_y^-1 y - 1/2 log |K_y| - n/2 log(2 pi) + * + * The matrix K_y = K_f + sigma_n^2 I + * and the quantity Theta refers to the hyperparameters. + * + * To facilitate faster optimization, this routine computes the evidence and + * its derivative wrt to the 2 hyperparameter values. + * From eqn 5.9, we have: + * + * \partial log( p(y|X,Theta) )\partial\theta_j + * = 1/2 Tr( (alpha alpha^T - K^-1)\partial K\partial\theta_j ) + * + * where alpha = K^-1 y + * + * We need to provide a better way of including the uncertainties. Here we just assume sigma_n + * + */ +//double ComputeEvidenceAndDerivs( gsl_vector *x, gsl_vector *y, double sigma_n, double sigma_f, double l ) { +void ComputeEvidenceAndDerivs( double log_sigma_f, double log_l, GprInfo *Info ) { + + + int n, i, j, Method; + double log_K_det, Trace, E1, E2, E3, val, xi, xj; + double sig, sigma_f, l; + gsl_vector *x, *y; + + x = Info->x; + y = Info->y; + + + sigma_f = exp( log_sigma_f ); + l = exp( log_l ); + + /* + * Compute the following matrix quantities: K, K_dl, K_dsig. + * The last two are the partial derivs: \partial K\partial\theta_j + */ + n = x->size; + +// gsl_matrix *K = gsl_matrix_calloc( n, n ); +// gsl_matrix *K_dl = gsl_matrix_calloc( n, n ); +// gsl_matrix *K_dsig = gsl_matrix_calloc( n, n ); + for ( i=0; isigma_n_2_vec, i ); + gsl_matrix_set( Info->K, i, j, symm_kernel_function( xi, xj, sigma_f, l ) + sig ); + } else { + gsl_matrix_set( Info->K, i, j, symm_kernel_function( xi, xj, sigma_f, l ) ); + } + gsl_matrix_set( Info->K_dl, i, j, symm_kernel_function_dk_dl( xi, xj, sigma_f, l ) ); + gsl_matrix_set( Info->K_dsig, i, j, symm_kernel_function_dk_dsig( xi, xj, sigma_f, l ) ); + } + } + + + /* + * Compute det(K) and K^-1 + * From a Cholesky decomp, det(K) = sum of squared diagonals. + */ + Method = ReverseNegativeEigenvalues; +Method = ZeroNegativeEigenvalues; +// gsl_matrix *K_inv = gsl_matrix_calloc( n, n ); + CholeskyLikeDecomp( Info->K, Info->K_inv, Method ); + for ( log_K_det = 0.0, i=0; iK_inv, i, i ); + log_K_det += log(val); + } + //log_K_det *= 2.0; + gsl_linalg_cholesky_invert( Info->K_inv ); + + + /* + * Compute -1/2 y^T K_inv y. First do r = K_inv y, then do -1/2 y^T r. + */ +// gsl_vector *r = gsl_vector_calloc( n ); + gsl_blas_dgemv( CblasNoTrans, 1.0, Info->K_inv, Info->y, 0.0, Info->r ); + gsl_blas_ddot( y, Info->r, &E1 ); + E1 *= -0.5; + + /* + * Compute -1/2 log|K| + */ + //E2 = -0.5*log(K_det); + E2 = -log_K_det; + + + /* + * Compute -n/2 log(2 pi) + */ + E3 = (double)n * -0.5 * log(2.0*M_PI); + + + /* + * Compute log( p(y|X,Theta) ) (log evidence) + */ + Info->logp = E1 + E2 + E3; + printf("sigma_f, l = %g %g E1, E2, E3 = %g %g %g\n", sigma_f, l, E1, E2, E3); +Info->logp *= -1.0;; + + + + /* + * Compute the alpha alpha^T - K_inv matrix. Note that + * K_inv y was computed as r above. + * dger computes a x y^T + A, so for convenience, lets + * compute -1.0 r r^T + K_inv (which is the negative of what we want) + */ +// gsl_matrix *A = gsl_matrix_calloc( n, n ); + gsl_matrix_memcpy( Info->A, Info->K_inv ); // initialize A with K_inv + gsl_blas_dger( -1.0, Info->r, Info->r, Info->A ); // this is now -1*( alpha alpha^T - K_inv ) + + + + /* + * Now compute the derivs. + * dgemm computes aAB + bC + * set a=-1 to revrse sign to fix sign change above + */ +// gsl_matrix *C = gsl_matrix_calloc( n, n ); + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dl, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_l = l * 0.5*Trace; + //Info->dlogp_dlog_l = 0.5*Trace; +Info->dlogp_dlog_l *= -1.0; + + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dsig, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_sig = sigma_f * 0.5*Trace; + //Info->dlogp_dlog_sig = 0.5*Trace; +Info->dlogp_dlog_sig *= -1.0; + + + + + /* + * We are done. The 3 quantities we are after are; logp, dlogp_dl, dlogo_dsig. + */ + //printf("l, sigma_f = %g %g logp, dlogp_dl, dlogp_dsig = %g %g %g\n", l, sigma_f, Info->logp, Info->dlogp_dl, Info->dlogp_dsig); + + return( Info->logp ); + + +} + + +/* + * The x's here are the hyper-parameters not the x's of the Gpr function + */ +void HyperParam_FdF( gsl_vector *x, void *Data, double *F, gsl_vector *dF ) { + + double log_sigma_f, log_l; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); + log_l = gsl_vector_get( x, 1 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// *F = GSL_NAN; +// gsl_vector_set( dF, 0, GSL_NAN ); +// gsl_vector_set( dF, 1, GSL_NAN ); +// } + + ComputeEvidenceAndDerivs( log_sigma_f, log_l, g ); + + *F = g->logp; + gsl_vector_set( dF, 0, g->dlogp_dlog_sig ); + gsl_vector_set( dF, 1, g->dlogp_dlog_l ); + //printf("HyperParam_FdF: log_sigma_f, log_l = %g %g, F, dF = %g %g %g\n", log_sigma_f, log_l, g->logp, g->dlogp_dlog_sig, g->dlogp_dlog_l ); + + +} +double HyperParam_F( gsl_vector *x, void *Data ) { + + double log_sigma_f, log_l; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); + log_l = gsl_vector_get( x, 1 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// return( GSL_NAN ); +// } + + ComputeEvidenceAndDerivs( log_sigma_f, log_l, g ); + //printf("HyperParam_F: log_sigma_f, log_l = %g %g, F = %g\n", log_sigma_f, log_l, g->logp ); + + + return( g->logp ); + +} +void HyperParam_dF( gsl_vector *x, void *Data, gsl_vector *dF ) { + + double log_sigma_f, log_l; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); + log_l = gsl_vector_get( x, 1 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// gsl_vector_set( dF, 0, GSL_NAN ); +// gsl_vector_set( dF, 1, GSL_NAN ); +// } + + ComputeEvidenceAndDerivs( log_sigma_f, log_l, g ); + + gsl_vector_set( dF, 0, g->dlogp_dlog_sig ); + gsl_vector_set( dF, 1, g->dlogp_dlog_l ); + //printf("HyperParam_dF: log_sigma_f, log_l = %g %g, dF = %g %g\n", log_sigma_f, log_l, g->dlogp_dlog_sig, g->dlogp_dlog_l ); + + +} + + +void compute_cov_matrices( gsl_vector *x, gsl_vector *x_star, double sigma_f, double l, gsl_matrix *K, gsl_matrix *K_star2, gsl_matrix *K_star ) { + + int i, j, n, n_star; + double xx; + + n = x->size; + n_star = x_star->size; + + for ( i=0; isize1; + if ( n != A_orig->size2 ){ + printf("A matrix is not square.\n"); + return(0); + } + + if ( n < 2 ){ + printf("A matrix size is <= 1.\n"); + return(0); + } + + /* + * Perform eignevalue decomposition (Compute Lambda and U ) + */ + gsl_vector *eval = gsl_vector_calloc( n ); + gsl_vector *eval_prime = gsl_vector_calloc( n ); + gsl_matrix *A = gsl_matrix_calloc( n, n ); gsl_matrix_memcpy( A, A_orig ); + gsl_matrix *U = gsl_matrix_calloc( n, n ); + gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc( n ); + gsl_eigen_symmv( A, eval, U, w ); gsl_eigen_symmv_free( w ); + + + // set all negative eignevalues to zero (or small positive number?) +// If we get a negative eignevalue, then first try to switch its sign and also +// the sign of the eigenvector. + + for (i=0; i ymax) ymax = yin[n]; + ++n; +} +fclose(fp_in); +// make pitch angle symmetric across 90. +//for (i=0; isigma_n = 0.1; +Info->sigma_n_2 = Info->sigma_n*Info->sigma_n; +for (i=0; ix, i, xin[i]-90.0 ); + gsl_vector_set( Info->y, i, yin[i]/ymax ); + sig = dyin[i]/ymax; + gsl_vector_set( Info->sigma_n_vec, i, sig ); + gsl_vector_set( Info->sigma_n_2_vec, i, sig*sig ); +} + + + + + if (0==1){ + d = 1; + n = 100; + + + + InitGprInfo( n, Info ); + + // Set Error std-dev + Info->sigma_n = 0.4; + Info->sigma_n_2 = Info->sigma_n*Info->sigma_n; + + //dx = 1.0/(double)(n-1); + dx = 0.8/(double)(n-1); + f = gsl_vector_calloc( n ); + for ( i=0; ix, i, val ); + gsl_vector_set( f, i, func(val) ); + } + + fp_out = fopen( "Fofx.dat", "w" ); + for (i=0; ix, i ), gsl_vector_get( f, i ) ); + } + fclose( fp_out ); + + /* + * Compute Gaussian errors + */ + gsl_vector *epsilon = gsl_vector_calloc( n ); + for (i=0; isigma_n ); + gsl_vector_set( epsilon, i, eps ); + gsl_vector_set( Info->y, i, gsl_vector_get( f, i ) + eps ); + } + gsl_vector_free( f ); + gsl_vector_free( epsilon ); +} + + fp_out = fopen( "SampleData.dat", "w" ); + for (i=0; ix, i ), gsl_vector_get( Info->y, i ) ); + } + fclose( fp_out ); + + + + + + + + /* + * Determine optimal hyper-parameteres: sigma_f, l. + */ + gsl_multimin_function_fdf HyperParam_Func; + HyperParam_Func.n = 2; + HyperParam_Func.f = &HyperParam_F; + HyperParam_Func.df = &HyperParam_dF; + HyperParam_Func.fdf = &HyperParam_FdF; + HyperParam_Func.params = (void *)Info; + + // starting point + gsl_vector *hyper = gsl_vector_calloc( 2 ); + gsl_vector_set( hyper, 0, log(2.0) ); // starting point for log_sigma_f + gsl_vector_set( hyper, 1, log(0.4) ); // starting point for log_l +gsl_vector_set( hyper, 1, log(20.) ); // starting point for log_l + //gsl_vector_set( hyper, 0, (2.0) ); // starting point for log_sigma_f + //gsl_vector_set( hyper, 1, (0.4) ); // starting point for log_l + + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_conjugate_fr; + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_conjugate_pr; + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs; + const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs2; + gsl_multimin_fdfminimizer *s = gsl_multimin_fdfminimizer_alloc( T, 2 ); + gsl_multimin_fdfminimizer_set( s, &HyperParam_Func, hyper, 0.01, 1e-2 ); + + + + size_t iter = 0; + int status; + do { + iter++; + status = gsl_multimin_fdfminimizer_iterate( s ); + if (status) break; + status = gsl_multimin_test_gradient( s->gradient, 1e-2 ); + if (status == GSL_SUCCESS) printf( "Minimum found at:\n" ); + printf( "%5d %.5f %.5f %10.5f\n", iter, gsl_vector_get( s->x, 0 ), gsl_vector_get( s->x, 1 ), s->f ); + } while ( (status == GSL_CONTINUE) && (iter < 100) ); + + gsl_vector *best_hyper; + best_hyper = gsl_multimin_fdfminimizer_x( s ); // best_hyer doesnt need to be free'd +// Info->log_sigma_f = fabs( gsl_vector_get( best_hyper, 0 ) ); +// Info->log_l = fabs( gsl_vector_get( best_hyper, 1 ) ); +Info->log_sigma_f = gsl_vector_get( best_hyper, 0 ); +Info->log_l = gsl_vector_get( best_hyper, 1 ); + Info->sigma_f = exp( Info->log_sigma_f ); + Info->l = exp( Info->log_l ); +// Info->sigma_f = ( Info->log_sigma_f ); +// Info->l = ( Info->log_l ); +//Info->sigma_f = .20; +//Info->l = 28.0; +//Info->l = 5.0; +printf("sigma_f, l = %g %g\n", Info->sigma_f, Info->l); + gsl_multimin_fdfminimizer_free( s ); + gsl_vector_free( hyper ); + + + + + + + + int n_star; + double **My_K, **My_K_star2, **My_K_star; + + n_star = 100; +// Info->l = 0.100259; +// Info->sigma_f = 2.22554; + + gsl_vector *x_star = gsl_vector_calloc( n_star ); + //LGM_ARRAY_2D( My_K, n, n, double ); + //LGM_ARRAY_2D( My_K_star2, n_star, n_star, double ); + //LGM_ARRAY_2D( My_K_star, n_star, n, double ); + +//dx = 1.0/(double)(n_star-1); for ( i=0; ix, x_star, Info->sigma_f, Info->l, K, K_star2, K_star ); + + //DumpGif3( "K_star2", n_star, n_star, My_K_star2 ); + //DumpGif3( "K_star", n_star, n, My_K_star ); + //DumpGif3( "K", n, n, My_K ); + + + /* + * Do a Cholesky-like decomposition + */ + gsl_matrix *L = gsl_matrix_calloc( n_star, n_star ); + CholeskyLikeDecomp( K_star2, L, Method ); + + + /* + * Assume means are zero + */ + gsl_vector *mu = gsl_vector_calloc( n_star ); + for (i=0; isigma_n_2_vec, i ) ); + } + CholeskyLikeDecomp( A, L1, Method ); + gsl_linalg_cholesky_invert( L1 ); + + // Get f_bar_star = K(x_star, x) ( K(x,x) + sigma_n^2 I )^-1 y + gsl_matrix *R = gsl_matrix_calloc( n_star, n ); // zero matrix + gsl_vector *f_bar_star = gsl_vector_calloc( n_star ); + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, K_star, L1, 0.0, R ); + gsl_blas_dgemv( CblasNoTrans, 1.0, R, Info->y, 0.0, f_bar_star ); + + + // Get cov_f_star = K_star2 - K_star ( K + sigma_n^2 I )^-1 K_star^T + gsl_matrix *R2 = gsl_matrix_calloc( n, n_star ); // zero matrix + gsl_matrix *cov_f_star = gsl_matrix_calloc( n_star, n_star ); + gsl_matrix_memcpy( cov_f_star, K_star2 ); + gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1.0, L1, K_star, 0.0, R2 ); + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, K_star, R2, 1.0, cov_f_star ); + + // Decompose the cov_f_star + gsl_matrix *cov_f_star_L = gsl_matrix_calloc( n_star, n_star ); + CholeskyLikeDecomp( cov_f_star, cov_f_star_L, Method ); + + + + /* + * Allocate space for the final avg and credibility intervals. + * And for workspace variables to compute avg and std-dev. + */ + Info->y_hat = gsl_vector_calloc( n_star ); + Info->y_cred_lo = gsl_vector_calloc( n_star ); + Info->y_cred_hi = gsl_vector_calloc( n_star ); + + int n_samples = 1000; + double **y_dist, yavg, ystd; + LGM_ARRAY_2D( y_dist, n_star, n_samples, double ); + + /* + * Draw some sample priors + */ + fp_out = fopen( "Zofx_star.dat", "w" ); + for ( i=0; iy_hat, ii, yavg ); + gsl_vector_set( Info->y_cred_lo, ii, yavg - 2.0*ystd ); + gsl_vector_set( Info->y_cred_hi, ii, yavg + 2.0*ystd ); + } + + LGM_ARRAY_2D_FREE( y_dist ); + + + fp_out = fopen( "y_hat.dat", "w" ); + for (ii=0; iiy_hat, ii ) ); + fclose( fp_out ); + + fp_out = fopen( "y_cred_lo.dat", "w" ); + for (ii=0; iiy_cred_lo, ii ) ); + fclose( fp_out ); + + fp_out = fopen( "y_cred_hi.dat", "w" ); + for (ii=0; iiy_cred_hi, ii ) ); + fclose( fp_out ); + + + + + + + /* + * Cleanup + */ + gsl_vector_free( z_star ); + gsl_vector_free( mu ); + gsl_vector_free( f_bar_star ); + + gsl_matrix_free( K ); + gsl_matrix_free( K_star ); + gsl_matrix_free( K_star2 ); + gsl_matrix_free( L ); + gsl_matrix_free( A ); + gsl_matrix_free( L1 ); + gsl_matrix_free( R ); + gsl_matrix_free( R2 ); + gsl_matrix_free( cov_f_star ); + gsl_matrix_free( cov_f_star_L ); + + gsl_rng_free( rng ); + + //LGM_ARRAY_2D_FREE( My_K ); + //LGM_ARRAY_2D_FREE( My_K_star2 ); + //LGM_ARRAY_2D_FREE( My_K_star ); + + return(0); + +} diff --git a/libLanlGeoMag/GPR_v3.c b/libLanlGeoMag/GPR_v3.c new file mode 100644 index 000000000..56067d0ae --- /dev/null +++ b/libLanlGeoMag/GPR_v3.c @@ -0,0 +1,972 @@ +#include "Lgm/GPR.h" + +double kernel_function( double x, double y, double sigma_f, double l, double sigma_b, double sigma_nu, double c ) { + + double diff, Norm2; + double k_rbf, k_lin; + + /* + * Compute the norm for x-y. norm = sqrt( sum( (x[i]-y[i])^2 ) ^1/2 + * since we end up squaring the norm later dont need sqrt. + */ + diff = x - y; + Norm2 = diff*diff; + // Norm = sqrt(Norm2); + + k_rbf = sigma_f * exp( -Norm2 / (2.0*l*l) ); +// k_lin = sigma_b*sigma_b + sigma_nu*sigma_nu*(x-c)*(y-c); + + return( k_rbf + k_lin ); + +} + +/* + * Derivs for k_lin + */ +double kernel_function_dk_dsigb( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( 2.0*sigma_b ); +} + +double kernel_function_dk_dsignu( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( 2.0*sigma_nu*(x-c)*(y-c) ); +} + +double kernel_function_dk_dc( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( -sigma_nu*sigma_nu*( x + y - 2.0*c ) ); +} + + + + +/* + * Derivs for k_rbf + */ +double kernel_function_dk_dsig( double x, double y, double sigma_f, double l ) { + + double diff, Norm2; + + /* + * Compute the norm for x-y. norm = sqrt( sum( (x[i]-y[i])^2 ) ^1/2 + * since we end up squaring the norm later dont need sqrt. + */ + diff = x - y; + Norm2 = diff*diff; + // Norm = sqrt(Norm2); + + return( exp( -Norm2 / (2.0*l*l) ) ); + +} + +double kernel_function_dk_dl( double x, double y, double sigma_f, double l ) { + + double diff, Norm2; + double l2; + + /* + * Compute the norm for x-y. norm = sqrt( sum( (x[i]-y[i])^2 ) ^1/2 + * since we end up squaring the norm later dont need sqrt. + */ + diff = x - y; + Norm2 = diff*diff; + l2 = l*l; + // Norm = sqrt(Norm2); + + return( Norm2*sigma_f/(l*l2) * exp( -Norm2 / (2.0*l2) ) ); + +} + + +/* + * This is a composite kernel function k = k(x, x') + k(x',x) + * which should enforce symmetry + */ +double symm_kernel_function( double x, double y, double sigma_f, double l, double sigma_b, double sigma_nu, double c ) { + return( kernel_function( x, y, sigma_f, l, sigma_b, sigma_nu, c ) + kernel_function( -x, y, sigma_f, l, sigma_b, sigma_nu, c ) ); +} +double symm_kernel_function_dk_dsig( double x, double y, double sigma_f, double l ) { + return( kernel_function_dk_dsig( x, y, sigma_f, l ) + kernel_function_dk_dsig( -x, y, sigma_f, l ) ); +} +double symm_kernel_function_dk_dl( double x, double y, double sigma_f, double l ) { + return( kernel_function_dk_dl( x, y, sigma_f, l ) + kernel_function_dk_dl( -x, y, sigma_f, l ) ); +} +double symm_kernel_function_dk_dsigb( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( kernel_function_dk_dsigb( x, y, sigma_b, sigma_nu, c ) + kernel_function_dk_dsigb( -x, y, sigma_b, sigma_nu, c ) ); +} +double symm_kernel_function_dk_dsignu( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( kernel_function_dk_dsignu( x, y, sigma_b, sigma_nu, c ) + kernel_function_dk_dsignu( -x, y, sigma_b, sigma_nu, c ) ); +} +double symm_kernel_function_dk_dc( double x, double y, double sigma_b, double sigma_nu, double c ) { + return( kernel_function_dk_dc( x, y, sigma_b, sigma_nu, c ) + kernel_function_dk_dc( -x, y, sigma_b, sigma_nu, c ) ); +} + + + + + +GprInfo *InitGprInfo( int n, int n_star, int ForceSymmetry ) { + + GprInfo *Info = calloc( 1, sizeof(*Info) ); + + Info->ForceSymmetry = ( ForceSymmetry > 0 ) ? 1 : 0; + + + Info->n = n; + Info->x = gsl_vector_calloc( n ); + Info->y = gsl_vector_calloc( n ); + Info->sigma_n_vec = gsl_vector_calloc( n ); + Info->sigma_n_2_vec = gsl_vector_calloc( n ); + + Info->K = gsl_matrix_calloc( n, n ); + Info->K_dl = gsl_matrix_calloc( n, n ); + Info->K_dsig = gsl_matrix_calloc( n, n ); + Info->K_dsigb = gsl_matrix_calloc( n, n ); + Info->K_dsignu = gsl_matrix_calloc( n, n ); + Info->K_dc = gsl_matrix_calloc( n, n ); + Info->K_inv = gsl_matrix_calloc( n, n ); + Info->r = gsl_vector_calloc( n ); + Info->A = gsl_matrix_calloc( n, n ); + Info->C = gsl_matrix_calloc( n, n ); + + + Info->n_star = n_star; + Info->x_star = gsl_vector_calloc( n_star ); + Info->y_hat = gsl_vector_calloc( n_star ); + Info->y_cred_lo = gsl_vector_calloc( n_star ); + Info->y_cred_hi = gsl_vector_calloc( n_star ); + + return( Info ); + +} + + +void FreeGprInfo( GprInfo *Info ) { + + + gsl_vector_free( Info->x ); + gsl_vector_free( Info->y ); + gsl_vector_free( Info->sigma_n_vec ); + gsl_vector_free( Info->sigma_n_2_vec ); + + gsl_matrix_free( Info->K ); + gsl_matrix_free( Info->K_dl ); + gsl_matrix_free( Info->K_dsig ); + gsl_matrix_free( Info->K_dsigb ); + gsl_matrix_free( Info->K_dsignu ); + gsl_matrix_free( Info->K_dc ); + gsl_matrix_free( Info->K_inv ); + gsl_vector_free( Info->r ); + gsl_matrix_free( Info->A ); + gsl_matrix_free( Info->C ); + + gsl_vector_free( Info->x_star ); + gsl_vector_free( Info->y_hat ); + gsl_vector_free( Info->y_cred_lo ); + gsl_vector_free( Info->y_cred_hi ); + + free( Info ); + + return; + +} + + +/* + * For large matrices, the normal Cholesky decomposition seems to fail. + * + * This routine solves this as follows: + * + * 1. Compute eignevalue decomposition A = U Lambda U^T + * + * 2. Then set all negative eignevalues to zero (or small +ve number?) + * + * 3. Then consider A^prime = U Lambda^prime U^T + * + * 4. Note that this can be rewritten as A^prime = (U sqrt(Lambda^prime)) + * (U sqrt(Lambda^prime))^T + * + * 5. Also note that if you do a QR decomp on (U sqrt(Lambda^prime))^T ( to + * get = Q R ), then A^prime = (Q R)^T (Q R) = R^T R because Q is orthogonal. + * So Aprime = R^T R. + * + * 6. Use the L = R^T matrix to get a Cholesky-like decomp. One additional + * problem (potentially) is that in normal Cholesky Decomp, the diagonal + * elements are typical forced to be positive. If we find a negative + * diagonal in our L, we just multiply the whole column by -1. This should + * leave L L^T unchanged. + */ +int CholeskyLikeDecomp( gsl_matrix *A_orig, gsl_matrix *L, int Method ) { + + int n, i, j; + double e, val, val2; + + n = A_orig->size1; + if ( n != A_orig->size2 ){ + printf("A matrix is not square.\n"); + return(0); + } + + if ( n < 2 ){ + printf("A matrix size is <= 1.\n"); + return(0); + } + + /* + * Perform eignevalue decomposition (Compute Lambda and U ) + */ + gsl_vector *eval = gsl_vector_calloc( n ); + gsl_vector *eval_prime = gsl_vector_calloc( n ); + gsl_matrix *A = gsl_matrix_calloc( n, n ); gsl_matrix_memcpy( A, A_orig ); + gsl_matrix *U = gsl_matrix_calloc( n, n ); + gsl_eigen_symmv_workspace *w = gsl_eigen_symmv_alloc( n ); + gsl_eigen_symmv( A, eval, U, w ); gsl_eigen_symmv_free( w ); + + + // set all negative eignevalues to zero (or small positive number?) +// If we get a negative eignevalue, then first try to switch its sign and also +// the sign of the eigenvector. + + for (i=0; ix; + y = Info->y; + + + sigma_f = exp( log_sigma_f ); + l = exp( log_l ); + sigma_b = exp( log_sigma_b ); + sigma_nu = exp( log_sigma_nu ); + + /* + * Compute the following matrix quantities: K, K_dl, K_dsig. + * The last two are the partial derivs: \partial K\partial\theta_j + */ + n = x->size; + +// gsl_matrix *K = gsl_matrix_calloc( n, n ); +// gsl_matrix *K_dl = gsl_matrix_calloc( n, n ); +// gsl_matrix *K_dsig = gsl_matrix_calloc( n, n ); + for ( i=0; isigma_n_2_vec, i ); + if ( Info->ForceSymmetry ) { + gsl_matrix_set( Info->K, i, j, symm_kernel_function( xi, xj, sigma_f, l, sigma_b, sigma_nu, c ) + sig ); + } else { + gsl_matrix_set( Info->K, i, j, kernel_function( xi, xj, sigma_f, l, sigma_b, sigma_nu, c ) + sig ); + } + } else { + if ( Info->ForceSymmetry ) { + gsl_matrix_set( Info->K, i, j, symm_kernel_function( xi, xj, sigma_f, l, sigma_b, sigma_nu, c ) ); + } else { + gsl_matrix_set( Info->K, i, j, kernel_function( xi, xj, sigma_f, l, sigma_b, sigma_nu, c ) ); + } + } + if ( Info->ForceSymmetry ) { + gsl_matrix_set( Info->K_dl, i, j, symm_kernel_function_dk_dl( xi, xj, sigma_f, l ) ); +//printf("symm_kernel_function_dk_dl( %g, %g, %g, %g ) = %g\n", xi, xj, sigma_f, l, symm_kernel_function_dk_dl( xi, xj, sigma_f, l )); + gsl_matrix_set( Info->K_dsig, i, j, symm_kernel_function_dk_dsig( xi, xj, sigma_f, l ) ); +//printf("symm_kernel_function_dk_dsig( %g, %g, %g, %g ) = %g\n", xi, xj, sigma_f, l, symm_kernel_function_dk_dsig( xi, xj, sigma_f, l )); + gsl_matrix_set( Info->K_dsigb, i, j, symm_kernel_function_dk_dsigb( xi, xj, sigma_b, sigma_nu, c ) ); + gsl_matrix_set( Info->K_dsignu, i, j, symm_kernel_function_dk_dsignu( xi, xj, sigma_b, sigma_nu, c ) ); + gsl_matrix_set( Info->K_dc, i, j, symm_kernel_function_dk_dc( xi, xj, sigma_b, sigma_nu, c ) ); + } else { + gsl_matrix_set( Info->K_dl, i, j, kernel_function_dk_dl( xi, xj, sigma_f, l ) ); + gsl_matrix_set( Info->K_dsig, i, j, kernel_function_dk_dsig( xi, xj, sigma_f, l ) ); + gsl_matrix_set( Info->K_dsigb, i, j, kernel_function_dk_dsigb( xi, xj, sigma_b, sigma_nu, c ) ); + gsl_matrix_set( Info->K_dsignu, i, j, kernel_function_dk_dsignu( xi, xj, sigma_b, sigma_nu, c ) ); + gsl_matrix_set( Info->K_dc, i, j, kernel_function_dk_dc( xi, xj, sigma_b, sigma_nu, c ) ); + } + } + } + + + /* + * Compute det(K) and K^-1 + * From a Cholesky decomp, det(K) = sum of squared diagonals. + */ + Method = ReverseNegativeEigenvalues; +Method = ZeroNegativeEigenvalues; +// gsl_matrix *K_inv = gsl_matrix_calloc( n, n ); + CholeskyLikeDecomp( Info->K, Info->K_inv, Method ); + for ( log_K_det = 0.0, i=0; iK_inv, i, i ); + //printf("val = %g\n", val); + log_K_det += log(val); + } + //log_K_det *= 2.0; + gsl_linalg_cholesky_invert( Info->K_inv ); + + + /* + * Compute -1/2 y^T K_inv y. First do r = K_inv y, then do -1/2 y^T r. + */ +// gsl_vector *r = gsl_vector_calloc( n ); + gsl_blas_dgemv( CblasNoTrans, 1.0, Info->K_inv, Info->y, 0.0, Info->r ); + gsl_blas_ddot( y, Info->r, &E1 ); + E1 *= -0.5; + + /* + * Compute -1/2 log|K| + */ + //E2 = -0.5*log(K_det); + E2 = -log_K_det; + + + /* + * Compute -n/2 log(2 pi) + */ + E3 = (double)n * -0.5 * log(2.0*M_PI); + + + /* + * Compute log( p(y|X,Theta) ) (log evidence) + */ + Info->logp = E1 + E2 + E3; + if (VERBOSE > 0 ) printf("sigma_f, l = %g %g E1, E2, E3 = %g %g %g\n", sigma_f, l, E1, E2, E3); + Info->logp *= -1.0;; + + + + /* + * Compute the alpha alpha^T - K_inv matrix. Note that + * K_inv y was computed as r above. + * dger computes a x y^T + A, so for convenience, lets + * compute -1.0 r r^T + K_inv (which is the negative of what we want) + */ +// gsl_matrix *A = gsl_matrix_calloc( n, n ); + gsl_matrix_memcpy( Info->A, Info->K_inv ); // initialize A with K_inv + gsl_blas_dger( -1.0, Info->r, Info->r, Info->A ); // this is now -1*( alpha alpha^T - K_inv ) + + + + /* + * Now compute the derivs. + * dgemm computes aAB + bC + * set a=-1 to revrse sign to fix sign change above + */ +// gsl_matrix *C = gsl_matrix_calloc( n, n ); + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dl, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_l = l * 0.5*Trace; + //Info->dlogp_dlog_l = 0.5*Trace; +Info->dlogp_dlog_l *= -1.0; + + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dsig, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_sig = sigma_f * 0.5*Trace; + //Info->dlogp_dlog_sig = 0.5*Trace; +Info->dlogp_dlog_sig *= -1.0; + + + + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dsigb, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_sigb = sigma_b * 0.5*Trace; + Info->dlogp_dlog_sigb *= -1.0; + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dsignu, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dlog_signu = sigma_nu * 0.5*Trace; + Info->dlogp_dlog_signu *= -1.0; + + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, Info->A, Info->K_dc, 0.0, Info->C ); + for ( Trace = 0.0, i=0; iC, i, i ); + Info->dlogp_dc = 0.5*Trace; + Info->dlogp_dc *= -1.0; + + + /* + * We are done. The 3 quantities we are after are; logp, dlogp_dl, dlogo_dsig. + */ +/* + +printf("l, sigma_f, sigma_b, sigma_nu, c = %g %g %g %g %g logp, dlogp_dl, dlogp_dsig, dlogp_dlog_sigb, dlogp_dsig, dlogp_dc = %g %g %g %g %g %g\n", +l, sigma_f, sigma_b, sigma_nu, c, +Info->logp, +Info->dlogp_dlog_l, Info->dlogp_dlog_sig, +Info->dlogp_dlog_sigb, Info->dlogp_dlog_signu, Info->dlogp_dc ); + + +*/ + + return; + + +} + + +/* + * The x's here are the hyper-parameters not the x's of the Gpr function + */ +void HyperParam_FdF( const gsl_vector *x, void *Data, double *F, gsl_vector *dF ) { + + double log_sigma_f, log_l; + double log_sigma_b, log_sigma_nu, c; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); +//log_l = log( 1.0 ); + log_l = gsl_vector_get( x, 1 ); +// log_sigma_b = gsl_vector_get( x, 2 ); +// log_sigma_nu = gsl_vector_get( x, 3 ); +// c = gsl_vector_get( x, 4 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// *F = GSL_NAN; +// gsl_vector_set( dF, 0, GSL_NAN ); +// gsl_vector_set( dF, 1, GSL_NAN ); +// } + + ComputeEvidenceAndDerivs( log_sigma_f, log_l, log_sigma_b, log_sigma_nu, c, g ); + + *F = g->logp; + gsl_vector_set( dF, 0, g->dlogp_dlog_sig ); + gsl_vector_set( dF, 1, g->dlogp_dlog_l ); +// gsl_vector_set( dF, 2, g->dlogp_dlog_sigb ); +// gsl_vector_set( dF, 3, g->dlogp_dlog_signu ); +// gsl_vector_set( dF, 4, g->dlogp_dc ); + //printf("HyperParam_FdF: log_sigma_f, log_l = %g %g, F, dF = %g %g %g\n", log_sigma_f, log_l, g->logp, g->dlogp_dlog_sig, g->dlogp_dlog_l ); + + +} +double HyperParam_F( const gsl_vector *x, void *Data ) { + + double log_sigma_f, log_l, log_sigma_b, log_sigma_nu, c; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); + log_l = gsl_vector_get( x, 1 ); +// log_sigma_b = gsl_vector_get( x, 2 ); +// log_sigma_nu = gsl_vector_get( x, 3 ); +// c = gsl_vector_get( x, 4 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// return( GSL_NAN ); +// } + +//log_l = log( 20.0 ); + ComputeEvidenceAndDerivs( log_sigma_f, log_l, log_sigma_b, log_sigma_nu, c, g ); + //printf("HyperParam_F: log_sigma_f, log_l = %g %g, F = %g\n", log_sigma_f, log_l, g->logp ); + + + return( g->logp ); + +} +void HyperParam_dF( const gsl_vector *x, void *Data, gsl_vector *dF ) { + + double log_sigma_f, log_l, log_sigma_b, log_sigma_nu, c; + + /* + * Typecast the Data pointer to our GprInfo Structure type. + */ + GprInfo *g = (GprInfo *)Data; + + /* + * Extract the hyper parameters for this call + */ + log_sigma_f = gsl_vector_get( x, 0 ); + log_l = gsl_vector_get( x, 1 ); +// log_sigma_b = gsl_vector_get( x, 2 ); +// log_sigma_nu = gsl_vector_get( x, 3 ); +// c = gsl_vector_get( x, 4 ); + +// if ( (fabs(l) < 1e-6) || (fabs(sigma_f) < 1e-6) ) { +// gsl_vector_set( dF, 0, GSL_NAN ); +// gsl_vector_set( dF, 1, GSL_NAN ); +// } + +//log_l = log( 20.0 ); + ComputeEvidenceAndDerivs( log_sigma_f, log_l, log_sigma_b, log_sigma_nu, c, g ); + + gsl_vector_set( dF, 0, g->dlogp_dlog_sig ); + gsl_vector_set( dF, 1, g->dlogp_dlog_l ); +// gsl_vector_set( dF, 2, g->dlogp_dlog_sigb ); +// gsl_vector_set( dF, 3, g->dlogp_dlog_signu ); +// gsl_vector_set( dF, 4, g->dlogp_dc ); + //printf("HyperParam_dF: log_sigma_f, log_l = %g %g, dF = %g %g\n", log_sigma_f, log_l, g->dlogp_dlog_sig, g->dlogp_dlog_l ); + + +} + +//void compute_cov_matrices( gsl_vector *x, gsl_vector *x_star, double sigma_f, double l, gsl_matrix *K, gsl_matrix *K_star2, gsl_matrix *K_star ) { +void compute_cov_matrices( double sigma_f, double l, double sigma_b, double sigma_nu, double c, gsl_matrix *K, gsl_matrix *K_star2, gsl_matrix *K_star, GprInfo *Info ) { + + int i, j, n, n_star; + double xx; + + n = Info->x->size; + n_star = Info->x_star->size; + + for ( i=0; ix, i ); + for ( j=0; jForceSymmetry ) { + gsl_matrix_set( K, i, j, symm_kernel_function( xx, gsl_vector_get( Info->x, j), sigma_f, l, sigma_b, sigma_nu, c ) ); + } else { + gsl_matrix_set( K, i, j, kernel_function( xx, gsl_vector_get( Info->x, j), sigma_f, l, sigma_b, sigma_nu, c ) ); + } + } + } + + for ( i=0; ix_star, i ); + for ( j=0; jForceSymmetry ) { + gsl_matrix_set( K_star2, i, j, symm_kernel_function( xx, gsl_vector_get( Info->x_star, j), sigma_f, l, sigma_b, sigma_nu, c ) ); + } else { + gsl_matrix_set( K_star2, i, j, kernel_function( xx, gsl_vector_get( Info->x_star, j), sigma_f, l, sigma_b, sigma_nu, c ) ); + } + } + } + + for ( i=0; ix_star, i ); + for ( j=0; jForceSymmetry ) { + gsl_matrix_set( K_star, i, j, symm_kernel_function( xx, gsl_vector_get( Info->x, j ), sigma_f, l, sigma_b, sigma_nu, c ) ); + } else { + gsl_matrix_set( K_star, i, j, kernel_function( xx, gsl_vector_get( Info->x, j ), sigma_f, l, sigma_b, sigma_nu, c ) ); + } + } + } + +} + + + + + +double func( double x ) { + double s, f; +// return( sin(4.0*M_PI*x) + sin(7.0*M_PI*x) ); + + s = sin( M_PI*x ); + f = pow( s, 6.7 )+0.1; + + return( f ); +} + + + + + + +void GPR( GprInfo *Info ){ + + + int i, ii, n, n_star; + FILE *fp_out; + int Method; + gsl_rng *rng = gsl_rng_alloc( gsl_rng_taus ); + + + Method = ZeroNegativeEigenvalues; + + n = Info->n; + n_star = Info->n_star; + + + /* + * Determine optimal hyper-parameteres: sigma_f, l. + */ + gsl_multimin_function_fdf HyperParam_Func; + HyperParam_Func.n = 2; + HyperParam_Func.f = &HyperParam_F; + HyperParam_Func.df = &HyperParam_dF; + HyperParam_Func.fdf = &HyperParam_FdF; + HyperParam_Func.params = (void *)Info; + + // starting point + gsl_vector *hyper = gsl_vector_calloc( 2 ); + gsl_vector_set( hyper, 0, log(0.05) ); // starting point for log_sigma_f + gsl_vector_set( hyper, 1, log(20.0) ); // starting point for log_l +// gsl_vector_set( hyper, 2, log(0.1) ); // starting point for log_sigb +// gsl_vector_set( hyper, 3, log(0.1) ); // starting point for log_signu +// gsl_vector_set( hyper, 4, 0.0 ); // starting point for c + + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_conjugate_fr; + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_conjugate_pr; + //const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs; + const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs2; + gsl_multimin_fdfminimizer *s = gsl_multimin_fdfminimizer_alloc( T, 2 ); + gsl_multimin_fdfminimizer_set( s, &HyperParam_Func, hyper, 0.01, 1e-2 ); + + +if (0==1){ + int iter = 0; + int status; + do { + iter++; + status = gsl_multimin_fdfminimizer_iterate( s ); if (status) break; + status = gsl_multimin_test_gradient( s->gradient, 1e-4 ); if ((VERBOSE>0)&&(status == GSL_SUCCESS)) printf( "Minimum found at:\n" ); + //if (VERBOSE>0) printf( "%5d %.5f %.5f %10.5f\n", (int)iter, gsl_vector_get( s->x, 0 ), gsl_vector_get( s->x, 1 ), s->f ); + printf( "%5d %.5f %.5f %10.5f\n", (int)iter, gsl_vector_get( s->x, 0 ), gsl_vector_get( s->x, 1 ), s->f ); + } while ( (status == GSL_CONTINUE) && (iter < 1000) ); +printf("iter = %d\n", iter); +status = gsl_multimin_test_gradient( s->gradient, 1e-4 ); if ((VERBOSE>0)&&(status == GSL_SUCCESS)) printf( "Minimum found at:\n" ); +} + + gsl_vector *best_hyper; + best_hyper = gsl_multimin_fdfminimizer_x( s ); // best_hyer doesnt need to be free'd + Info->log_sigma_f = gsl_vector_get( best_hyper, 0 ); +// if ( Info->log_sigma_f > 20.0 ) Info->log_sigma_f = 20.0; + + Info->log_l = gsl_vector_get( best_hyper, 1 ); +// Info->log_sigma_b = gsl_vector_get( best_hyper, 2 ); +// Info->log_sigma_nu = gsl_vector_get( best_hyper, 3 ); +// Info->c = gsl_vector_get( best_hyper, 4 ); + + Info->sigma_f = exp( Info->log_sigma_f ); + Info->l = exp( Info->log_l ); +Info->sigma_f = 2.0; +Info->l = 0.1; + + + +//Info->sigma_f = 0.01; +//Info->l = 0.1; +//if ( Info->sigma_f > 1.0 ) Info->sigma_f = 1.0; +//Info->sigma_f = 0.1; +//Info->l = 20.0; +//Info->l = 45.0; +//Info->l = 60.0; +// Info->sigma_f = 1e6; +//Info->l = 10.0; +//Info->l = 10.0; +// Info->sigma_b = exp( Info->log_sigma_b ); +// Info->sigma_nu = exp( Info->log_sigma_nu ); +//Info->sigma_f = 0.5; +//if (Info->l < 0.25) Info->l = 0.25; + Info->sigma_b = 0.0; + Info->sigma_nu = 0.0; + Info->c = 0.0; + //if (VERBOSE>0) printf("sigma_f, l, sigma_b, sigma_nu, c = %g %g %g %g %g\n", Info->sigma_f, Info->l, Info->sigma_b, Info->sigma_nu, Info->c ); + printf("sigma_f, l, sigma_b, sigma_nu, c = %g %g %g %g %g\n", Info->sigma_f, Info->l, Info->sigma_b, Info->sigma_nu, Info->c ); +//Info->sigma_f = 10.0; +//Info->l = 30.0; + gsl_multimin_fdfminimizer_free( s ); + gsl_vector_free( hyper ); + + + /* + * This probably should be a user input -- user will want to specify points here. + */ +// int n_star; +// n_star = 100; +// gsl_vector *x_star = gsl_vector_calloc( n_star ); +// dx = 180.0/(double)(n_star-1); +// for ( i=0; isigma_f, Info->l, Info->sigma_b, Info->sigma_nu, Info->c, K, K_star2, K_star, Info ); + + + /* + * Do a Cholesky-like decomposition + */ + gsl_matrix *L = gsl_matrix_calloc( n_star, n_star ); + CholeskyLikeDecomp( K_star2, L, Method ); + + + /* + * Assume means are zero + */ + gsl_vector *mu = gsl_vector_calloc( n_star ); + for (i=0; ix_star, ii ), gsl_vector_get( z_star, ii ) ); + } + } + fclose( fp_out ); + } + + + /* + * Now, lets compute the posterior. + * + * f_star|X,y,X_star ~ N( fbar_star, cov(f_star) ) + * + * where, + * + * fbar_star = K(x_star, x) ( K(x,x) + sigma_n^2 I )^-1 y + * + * cov(f_star) = K(x_star, x_star) - K(x_star, x) ( K(x,x) + sigma_n^2 I )^-1 K(x,x_star) + * + * So the first thing we need to do is find the inverse; + * + * A = ( K(x,x) + sigma_n^2 I )^-1 + * + * + */ + gsl_matrix *A = gsl_matrix_calloc( n, n ); + gsl_matrix *L1 = gsl_matrix_calloc( n, n ); + + gsl_matrix_memcpy( A, K ); // copy over the whole K matrix + for (i=0; isigma_n_2_vec, i ) ); + } + CholeskyLikeDecomp( A, L1, Method ); + gsl_linalg_cholesky_invert( L1 ); + + // Get f_bar_star = K(x_star, x) ( K(x,x) + sigma_n^2 I )^-1 y + gsl_matrix *R = gsl_matrix_calloc( n_star, n ); // zero matrix + gsl_vector *f_bar_star = gsl_vector_calloc( n_star ); + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, 1.0, K_star, L1, 0.0, R ); + gsl_blas_dgemv( CblasNoTrans, 1.0, R, Info->y, 0.0, f_bar_star ); + + + // Get cov_f_star = K_star2 - K_star ( K + sigma_n^2 I )^-1 K_star^T + gsl_matrix *R2 = gsl_matrix_calloc( n, n_star ); // zero matrix + gsl_matrix *cov_f_star = gsl_matrix_calloc( n_star, n_star ); + gsl_matrix_memcpy( cov_f_star, K_star2 ); + gsl_blas_dgemm( CblasNoTrans, CblasTrans, 1.0, L1, K_star, 0.0, R2 ); + gsl_blas_dgemm( CblasNoTrans, CblasNoTrans, -1.0, K_star, R2, 1.0, cov_f_star ); + + // Decompose the cov_f_star + gsl_matrix *cov_f_star_L = gsl_matrix_calloc( n_star, n_star ); + CholeskyLikeDecomp( cov_f_star, cov_f_star_L, Method ); + + + + /* + * Allocate space for workspace variables to compute avg and std-dev. + */ + + int n_samples = 1000; + double **y_dist, yavg, ystd; + LGM_ARRAY_2D( y_dist, n_star, n_samples, double ); + + /* + * Draw some sample priors + */ + fp_out = fopen( "Zofx_star.dat", "w" ); + for ( i=0; ix_star, ii ), gsl_vector_get( z_star, ii ) ); + } + } + fclose( fp_out ); + + for (ii=0; iiy_hat, ii, yavg ); + gsl_vector_set( Info->y_cred_lo, ii, yavg - 2.0*ystd ); + gsl_vector_set( Info->y_cred_hi, ii, yavg + 2.0*ystd ); + } + + LGM_ARRAY_2D_FREE( y_dist ); + + + + /* + * Cleanup + */ + gsl_vector_free( z_star ); + gsl_vector_free( mu ); + gsl_vector_free( f_bar_star ); + + gsl_matrix_free( K ); + gsl_matrix_free( K_star ); + gsl_matrix_free( K_star2 ); + gsl_matrix_free( L ); + gsl_matrix_free( A ); + gsl_matrix_free( L1 ); + gsl_matrix_free( R ); + gsl_matrix_free( R2 ); + gsl_matrix_free( cov_f_star ); + gsl_matrix_free( cov_f_star_L ); + + + gsl_rng_free( rng ); + + return; + +} + + diff --git a/libLanlGeoMag/Lgm/GPR.h b/libLanlGeoMag/Lgm/GPR.h new file mode 100644 index 000000000..d6df4c7da --- /dev/null +++ b/libLanlGeoMag/Lgm/GPR.h @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZeroNegativeEigenvalues 1 +#define ReverseNegativeEigenvalues 0 + +#define VERBOSE 0 + +typedef struct GprInfo { + + + int ForceSymmetry; + + + double sigma_f, l; + double log_sigma_f, log_l; + + double sigma_b, sigma_nu; + double log_sigma_b, log_sigma_nu; + double c; + + int n; + gsl_vector *x; + gsl_vector *y; + gsl_vector *sigma_n_vec; + gsl_vector *sigma_n_2_vec; + double sigma_n; + double sigma_n_2; + + gsl_matrix *K; + gsl_matrix *K_dl; + gsl_matrix *K_dsig; + gsl_matrix *K_dsigb; + gsl_matrix *K_dsignu; + gsl_matrix *K_dc; + + gsl_matrix *K_inv; + gsl_vector *r; + gsl_matrix *A; + gsl_matrix *C; + + //double logp, dlogp_dsig, dlogp_dl; + double logp; + double dlogp_dlog_sig; + double dlogp_dlog_l; + double dlogp_dlog_sigb; + double dlogp_dlog_signu; + double dlogp_dc; + + + /* + * These hold the final mean and credibility intervals + */ + int n_star; + gsl_vector *x_star; + gsl_vector *y_hat; + gsl_vector *y_cred_lo; + gsl_vector *y_cred_hi; + +} GprInfo; + +GprInfo *InitGprInfo( int, int, int ); +void FreeGprInfo( GprInfo * ); +void ComputeEvidenceAndDerivs( double, double, double, double, double, GprInfo * ); +void GPR( GprInfo * ); + diff --git a/libLanlGeoMag/Lgm/Lgm_CTrans.h b/libLanlGeoMag/Lgm/Lgm_CTrans.h index a07132061..e513f3563 100644 --- a/libLanlGeoMag/Lgm/Lgm_CTrans.h +++ b/libLanlGeoMag/Lgm/Lgm_CTrans.h @@ -979,6 +979,7 @@ void Lgm_JensenCain1960( Lgm_Vector *v, Lgm_Vector *B, Lgm_CTrans *c ); void _Lgm_JensenCain1960( Lgm_Vector *v, Lgm_Vector *B, Lgm_CTrans *c ); +void Lgm_Terminator( double GLON, double *GLAT_N, int *nRoots, double alpha, Lgm_CTrans *c ); #endif diff --git a/libLanlGeoMag/Lgm/Lgm_DynamicMemory.h b/libLanlGeoMag/Lgm/Lgm_DynamicMemory.h index d86c4cd03..7d7f60e25 100644 --- a/libLanlGeoMag/Lgm/Lgm_DynamicMemory.h +++ b/libLanlGeoMag/Lgm/Lgm_DynamicMemory.h @@ -120,7 +120,7 @@ * Macros for dynamically allocating/freeing 1D arrays of any type */ #define LGM_ARRAY_1D( prow, col, type ) {\ - register type *pdata;\ + type *pdata;\ if ( col < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (col = %d)\n", (int)(col) ); exit(1); }\ pdata = (type *)calloc( (col), sizeof( type ) );\ if ( pdata == (type *)NULL ) {\ @@ -170,7 +170,7 @@ * Macros for dynamically allocating/freeing 3D arrays of any type */ #define LGM_ARRAY_3D( pgrid, grid, row, col, type ) {\ - register type **prow, *pdata;\ + type **prow, *pdata;\ int i;\ if ( row < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (row = %d)\n", (int)(row) ); exit(1); }\ if ( col < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (col = %d)\n", (int)(col) ); exit(1); }\ @@ -215,7 +215,7 @@ */ #define LGM_ARRAY_4D( pn4, n4, n3, n2, n1, type ) {\ long int i;\ - register type ***pn3, **pn2, *pdata;\ + type ***pn3, **pn2, *pdata;\ if ( n1 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n1 = %d)\n", (int)(n1) ); exit(1); }\ if ( n2 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n2 = %d)\n", (int)(n2) ); exit(1); }\ if ( n3 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n3 = %d)\n", (int)(n3) ); exit(1); }\ @@ -271,7 +271,7 @@ */ #define LGM_ARRAY_5D( pn5, n5, n4, n3, n2, n1, type ) {\ long int i;\ - register type ****pn4, ***pn3, **pn2, *pdata;\ + type ****pn4, ***pn3, **pn2, *pdata;\ if ( n1 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n1 = %d)\n", (int)(n1) ); exit(1); }\ if ( n2 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n2 = %d)\n", (int)(n2) ); exit(1); }\ if ( n3 < 1 ) { fprintf( stderr, "LGM_ARRAY_1D Macro: Trying to allocate less than one element (n3 = %d)\n", (int)(n3) ); exit(1); }\ @@ -383,7 +383,7 @@ * Macros for dynamically allocating/freeing 3D arrays of any type */ #define LGM_ARRAY_FROM_DATA_3D( pgrid, pdata, grid, row, col, type ) {\ - register type **prow;\ + type **prow;\ int i;\ type *pdata_tmp;\ if ( pdata == (type *)NULL ) {\ @@ -424,7 +424,7 @@ */ #define LGM_ARRAY_FROM_DATA_4D( pn4, pdata, n4, n3, n2, n1, type ) {\ long int i;\ - register type ***pn3, **pn2;\ + type ***pn3, **pn2;\ type *pdata_tmp;\ if ( pdata == (type *)NULL ) {\ fprintf(stderr, "LGM_ARRAY_FROM_DATA_4D Macro: pdata is NULL\n");\ @@ -475,7 +475,7 @@ */ #define LGM_ARRAY_FROM_DATA_5D( pn5, pdata, n5, n4, n3, n2, n1, type ) {\ long int i;\ - register type ****pn4, ***pn3, **pn2;\ + type ****pn4, ***pn3, **pn2;\ type *pdata_tmp;\ if ( pdata == (type *)NULL ) {\ fprintf(stderr, "LGM_ARRAY_FROM_DATA_5D Macro: pdata is NULL\n");\ diff --git a/libLanlGeoMag/Lgm/Lgm_FluxToPsd.h b/libLanlGeoMag/Lgm/Lgm_FluxToPsd.h index 5eed04d02..3b7cc6446 100644 --- a/libLanlGeoMag/Lgm/Lgm_FluxToPsd.h +++ b/libLanlGeoMag/Lgm/Lgm_FluxToPsd.h @@ -109,10 +109,22 @@ typedef struct Lgm_FluxToPsd { double *A; //!< Array of pitch angle values in Flux array. + double *Aeq; //!< Array of equatorial pitch angle values corresponding to the A values. + + double Beq; //!< Value of |B| on the current field line. + double **FLUX_EA; //!< Array of differential flux versus Energy and PitchAngle, Flux[E][A]. + double **dFLUX_EA; //!< Array of uncertainties in differential flux versus Energy and PitchAngle, Flux[E][A]. + double **PSD_EA; //!< Array of PSD versus Energy and PitchAngle, PSD[E][A]. + double **dPSD_EA; //!< Array of uncertainties in PSD versus Energy and PitchAngle, PSD[E][A]. + + double **PSD_EAeq; //!< Array of PSD versus Energy and Eq. PitchAngle, PSD[E][Aeq]. + + double **dPSD_EAeq; //!< Array of uncertainties in PSD versus Energy and Eq. PitchAngle, PSD[E][Aeq]. + int Alloced1; //!< If true, the arrays are alloced. @@ -126,6 +138,8 @@ typedef struct Lgm_FluxToPsd { double *AofK; //!< Array of Alpha values that are implied by the k values. Size is nK. + double *AEqofK; //!< Array of Eq. Alpha values that are implied by the k values. Size is nK. + double **EofMu; //!< Array of Energy values that are implied by the Mu, Alpha and B values. Size is nMu. double B; //!< Magnetic field strength. @@ -146,6 +160,8 @@ typedef struct Lgm_FluxToPsd { double **PSD_MK; //!< Array of PSD versus Mu and K, PSD[Mu][K]. + double **dPSD_MK; //!< Array of uncertainties in PSD versus Mu and K, PSD[Mu][K]. + int Alloced2; //!< If true, the arrays are alloced. @@ -253,11 +269,12 @@ typedef struct Lgm_PsdToFlux { // Flux -> PSD routines Lgm_FluxToPsd *Lgm_F2P_CreateFluxToPsd( int DumpDiagnostics ); void Lgm_F2P_FreeFluxToPsd( Lgm_FluxToPsd *f ); -void Lgm_F2P_SetFlux( double **J, double *E, int nE, double *A, int nA, Lgm_FluxToPsd *f ); +void Lgm_F2P_SetFlux( double **J, double **dJ, double *E, int nE, double *A, int nA, Lgm_FluxToPsd *f ); void Lgm_F2P_SetDateTimeAndPos( Lgm_DateTime *d, Lgm_Vector *u, Lgm_FluxToPsd *f ); void Lgm_F2P_SetObservedB( double B_obs, Lgm_FluxToPsd *f ); void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_MagModelInfo *mInfo, Lgm_FluxToPsd *f ); -double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_FluxToPsd *f ); +double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, double *dPsd, Lgm_FluxToPsd *f ); +double Lgm_F2P_GetPsdAtEandAlpha2( int iMu, double E, int iAEq, double *dPsd, Lgm_FluxToPsd *f ); // PSD -> Flux routines @@ -286,6 +303,8 @@ int Lgm_InterpArr( double *xa, double *ya, int n, double x, double *y ); double Model( double *x, int n, double E ); +double Lgm_DiffFluxToPsd2( double j, double m, double E ); + diff --git a/libLanlGeoMag/Lgm/Lgm_KdTree.h b/libLanlGeoMag/Lgm/Lgm_KdTree.h index 3eb7473cd..8e6cfeaba 100644 --- a/libLanlGeoMag/Lgm/Lgm_KdTree.h +++ b/libLanlGeoMag/Lgm/Lgm_KdTree.h @@ -123,8 +123,11 @@ void Lgm_KdTree_SubDivideVolume( Lgm_KdTreeNode *t, Lgm_KdTree *k /*! Store given N-dimensional data into a D-dimensional KD-tree data structure. */ Lgm_KdTree *Lgm_KdTree_Init( double **Positions, void **Objects, unsigned long int N, int D ) ; +Lgm_KdTree *Lgm_KdTree_CopyLite( Lgm_KdTree *ks ); +void Lgm_KdTree_FreeLite( Lgm_KdTree *kt ); Lgm_KdTreeNode *Lgm_CreateKdTreeRoot( int D ); +void Lgm_FreeKdTreeNode( Lgm_KdTreeNode *Node ); double Lgm_KdTree_MinDist( Lgm_KdTreeNode *Node, double *q ); int Lgm_KdTree_DoSearch( Lgm_KdTreeNode *Node, double *q, double md2 ); double Lgm_KdTree_InsertNode( Lgm_KdTreeNode *Node, double *q, Lgm_pQueue *PQ, double MaxDist2 ); diff --git a/libLanlGeoMag/Lgm/Lgm_LstarInfo.h b/libLanlGeoMag/Lgm/Lgm_LstarInfo.h index d33b7264a..3b1181db2 100644 --- a/libLanlGeoMag/Lgm/Lgm_LstarInfo.h +++ b/libLanlGeoMag/Lgm/Lgm_LstarInfo.h @@ -33,7 +33,7 @@ #define LGM_LSTAR_MOMENT_CDIP_2010 1 #define LGM_LSTAR_MOMENT_MCILWAIN 2 -#define LGM_LSTARINFO_MAX_FL 300 +#define LGM_LSTARINFO_MAX_FL 400 #define LGM_LSTARINFO_MAX_MINIMA 300 @@ -116,7 +116,6 @@ typedef struct Lgm_LstarInfo { int nMinMax; // Number of valid FLs represented in nMinima[] and nMaxima[] (we may have bailed early) int nMinima[ LGM_LSTARINFO_MAX_MINIMA ]; // # of minima on FL int nMaxima[ LGM_LSTARINFO_MAX_MINIMA ]; // # of maxima on FL (not including endpoints) - int nBounceRegions[ LGM_LSTARINFO_MAX_MINIMA ]; // # of bounce regions on FL (as particle may not be confined in a minimum) int nSplnPnts; double xa [ 3*LGM_LSTARINFO_MAX_FL ], ya[ 3*LGM_LSTARINFO_MAX_FL ], y2[ 3*LGM_LSTARINFO_MAX_FL ]; @@ -154,6 +153,8 @@ typedef struct Lgm_LstarInfo { double Earr[ 3*LGM_LSTARINFO_MAX_FL ]; // nominally the error on I-I0 (typically set to const). int nImI0; // number of vals stored. + int nBounceRegions[ LGM_LSTARINFO_MAX_FL ]; // # of bounce regions on FL + /* @@ -177,10 +178,10 @@ Lgm_LstarInfo *Lgm_CopyLstarInfo( Lgm_LstarInfo *s ); int Grad_I( Lgm_Vector *vin, Lgm_Vector *GradI, Lgm_LstarInfo *LstarInfo ); int ComputeVcg( Lgm_Vector *vin, Lgm_Vector *Vcg, Lgm_LstarInfo *LstarInfo ); int FindBmRadius( double Bm, double MLT, double mlat, double *r, double tol, Lgm_LstarInfo *LstarInfo ); -int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *mlat, double *rad, double mlat0, double mlat1, double mlat2, int *Iterations, Lgm_LstarInfo *LstarInfo ); -double ComputeI_FromMltMlat( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ); -double ComputeI_FromMltMlat1( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ); -double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, double I0, Lgm_LstarInfo *LstarInfo ); +int FindShellLine( double I0, double *Ifound, double Bm, double MLT, double *mlat, double *rad, double mlat0, double mlat1, double mlat2, int *Iterations, int RelaxTolerance, Lgm_LstarInfo *LstarInfo ); +double ComputeI_FromMltMlat( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ); +double ComputeI_FromMltMlat1( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ); +double ComputeI_FromMltMlat2( double Bm, double MLT, double mlat, double *r, double I0, int *ErrorStatus, Lgm_LstarInfo *LstarInfo ); void spline( double *x, double *y, int n, double yp1, double ypn, double *y2); void splint( double *xa, double *ya, double *y2a, int n, double x, double *y); void quicksort( unsigned long n, double *arr ); diff --git a/libLanlGeoMag/Lgm/Lgm_MagEphemInfo.h b/libLanlGeoMag/Lgm/Lgm_MagEphemInfo.h index d22e1df8f..faf2f2568 100644 --- a/libLanlGeoMag/Lgm/Lgm_MagEphemInfo.h +++ b/libLanlGeoMag/Lgm/Lgm_MagEphemInfo.h @@ -40,6 +40,7 @@ typedef struct Lgm_MagEphemInfo { int InOut; //!< Flag indicating whether we are inbound (-1) or outbound (+1) int OrbitNumber; //!< Orbit Number + double SunAngle; //!< Sun Angle (for RBSP its angle between Sun and Z axis in ZYZ frame) double Lat; //!< Geographic Latitude double Lon; //!< Geographic Longitude @@ -132,6 +133,8 @@ typedef struct Lgm_MagEphemInfo { double **ShellI; //!< [ PitchAngleIndex ][ FieldLineIndex ] Individual I values computed for each FL + int **nBounceRegions; //!< # of bounce regions on FL + /* * these are like the variables in the LstarInfo structure. Except they have an extra * dimension to hold pitch angle as well. @@ -197,6 +200,7 @@ typedef struct Lgm_MagEphemData { double *H5_TiltAngle; int *H5_InOut; int *H5_OrbitNumber; + double *H5_SunAngle; double **H5_Rgeo; double **H5_Rgeod; diff --git a/libLanlGeoMag/Lgm/Lgm_MagModelInfo.h b/libLanlGeoMag/Lgm/Lgm_MagModelInfo.h index 6a5fc3096..498f19ae7 100644 --- a/libLanlGeoMag/Lgm/Lgm_MagModelInfo.h +++ b/libLanlGeoMag/Lgm/Lgm_MagModelInfo.h @@ -78,7 +78,7 @@ #define LGM_IGRF 2 #define LGM_DUNGEY 3 -#define LGM_MAX_INTERP_PNTS 10000 +#define LGM_MAX_INTERP_PNTS 50000 #define LGM_RELATIVE_JUMP_METHOD 0 #define LGM_ABSOLUTE_JUMP_METHOD 1 @@ -102,6 +102,7 @@ #define LGM_EXTMODEL_TU82 14 #define LGM_EXTMODEL_OP88 15 +#define LGM_EXTMODEL_SCATTERED_DATA6 23 // Derivative schemes @@ -375,6 +376,7 @@ typedef struct Lgm_MagModelInfo { double Lgm_I_Integrator_epsrel; // Quadpack epsrel tolerance for I_integrator double Lgm_I_Integrator_epsabs; // Quadpack epsabs tolerance for I_integrator + /* * These variables are needed to make Sb_integrand() reentrant/thread-safe. * They basically used to be static declarations. @@ -391,6 +393,15 @@ typedef struct Lgm_MagModelInfo { + /* + * Variables to control FluxTubeVolume integration + */ + int Lgm_n_V_integrand_Calls; + int Lgm_V_Integrator; + + double Lgm_V_Integrator_epsrel; // Quadpack epsrel tolerance for V_integrator + double Lgm_V_Integrator_epsabs; // Quadpack epsabs tolerance for V_integrator + /* * Variables to control MagFlux integration @@ -443,12 +454,19 @@ typedef struct Lgm_MagModelInfo { double KdTree_kNN_MaxDist2; Lgm_KdTreeData *KdTree_kNN; int KdTree_kNN_Alloced; // number of elements allocated. (0 if unallocated). + int KdTreeCopy; // If set, then we assume the pointer + // to the tree data is a copy and we + // should not free it when calling + // Lgm_FreeMagInfo(). /* * hash table, etc. used in Lgm_B_FromScatteredData*() */ Lgm_DFI_RBF_Info *rbf_ht; // hash table (uthash) + double dfi_rbf_ht_size; // hash table size in MB + double dfi_rbf_ht_maxsize; // hash table max size in MB + CircularBuffer RBF_DFI_CB; Lgm_Vec_RBF_Info *vec_rbf_ht; // hash table (uthash) double vec_rbf_ht_size; // hash table size in MB @@ -529,11 +547,23 @@ typedef struct Lgm_MagModelInfo { //long int FP_nHashFinds; // Number of HASH_FIND()'s performed. //long int FP_nHashAdds; // Number of HASH_ADD_KEYPTR()'s performed. + /* + * Transformation matrix from GSM to PQB and (reverse) + */ + double Agsm_to_pqb[3][3]; + double Apqb_to_gsm[3][3]; + int Agsm_to_pqb_set; + void *Data; } Lgm_MagModelInfo; +void Lgm_GSM_TO_PQB( Lgm_Vector *u_gsm, Lgm_Vector *u_pqb, Lgm_MagModelInfo *m ); +void Lgm_Set_GSM_TO_PQB( Lgm_Vector *Position, Lgm_MagModelInfo *m ); +void Lgm_PQB_TO_GSM( Lgm_Vector *u_pqb, Lgm_Vector *u_gsm, Lgm_MagModelInfo *m ); + + typedef struct BrentFuncInfoP { @@ -759,12 +789,15 @@ int Lgm_B_FromScatteredData2( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In int Lgm_B_FromScatteredData3( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ); int Lgm_B_FromScatteredData4( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ); int Lgm_B_FromScatteredData5( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ); +int Lgm_B_FromScatteredData6( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ); void Lgm_B_FromScatteredData_SetUp( Lgm_MagModelInfo *Info ); void Lgm_B_FromScatteredData_TearDown( Lgm_MagModelInfo *Info ); void Lgm_B_FromScatteredData4_TearDown( Lgm_MagModelInfo *Info ); // unify the structs to avoid having this void Lgm_B_FromScatteredData5_SetUp( Lgm_MagModelInfo *Info ); void Lgm_B_FromScatteredData5_TearDown( Lgm_MagModelInfo *Info ); // I dont like this proliferation of routines here.. +void Lgm_B_FromScatteredData6_SetUp( Lgm_MagModelInfo *Info ); +void Lgm_B_FromScatteredData6_TearDown( Lgm_MagModelInfo *Info ); // I dont like this proliferation of routines here.. /* @@ -782,6 +815,8 @@ int Lgm_B_Dungey(Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info); /* * routines/functions for field integrals, invariants, etc. */ +double FluxTubeVolume( Lgm_MagModelInfo *fInfo ); +double V_integrand( double s, _qpInfo *qpInfo ); double Iinv( Lgm_MagModelInfo *fInfo ); double I_integrand( double s, _qpInfo *qpInfo ); double Iinv_interped( Lgm_MagModelInfo *fInfo ); @@ -872,16 +907,60 @@ int Lgm_CGM_TO_GEOD( double CgmLat, double CgmLon, double CgmRadi, double *geoL * Various B-related routines */ void Lgm_GradB( Lgm_Vector *u0, Lgm_Vector *GradB, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Grad_Divb( Lgm_Vector *u0, Lgm_Vector *Grad_Divb, int DerivScheme, double h, Lgm_MagModelInfo *m ); void Lgm_GradB2( Lgm_Vector *u0, Lgm_Vector *GradB, Lgm_Vector *GradB_para, Lgm_Vector *GradB_perp, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_GradBvec( Lgm_Vector *u0, double GradBvec[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_GradBvec2( int j, Lgm_Vector *u0, double GradBvec[4][4], int DerivScheme, double h, int n, double dt, Lgm_MagModelInfo **m ); void Lgm_CurlB( Lgm_Vector *u0, Lgm_Vector *CurlB, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Curlb( Lgm_Vector *u0, Lgm_Vector *Curlb, int DerivScheme, double h, Lgm_MagModelInfo *m ); void Lgm_CurlB2( Lgm_Vector *u0, Lgm_Vector *CurlB, Lgm_Vector *CurlB_para, Lgm_Vector *CurlB_perp, int DerivScheme, double h, Lgm_MagModelInfo *m ); void Lgm_B_Cross_GradB_Over_B( Lgm_Vector *u0, Lgm_Vector *A, int DerivScheme, double h, Lgm_MagModelInfo *m ); void Lgm_DivB( Lgm_Vector *u0, double *DivB, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Divb( Lgm_Vector *u0, double *Divb, int DerivScheme, double h, Lgm_MagModelInfo *m ); int Lgm_GradAndCurvDriftVel( Lgm_Vector *u0, Lgm_Vector *Vel, Lgm_MagModelInfo *m ); +void Lgm_dBdcomp( Lgm_Vector *u0, int comp, double *dBdcomp, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dBdx( Lgm_Vector *u0, double *dBx, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dBdy( Lgm_Vector *u0, double *dBy, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dBdz( Lgm_Vector *u0, double *dBz, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_LaplacianB( Lgm_Vector *u0, double *LaplacianB, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dDivbdcomp( Lgm_Vector *u0, int comp, double *dDivbdcomp, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Curvature( Lgm_Vector *q, Lgm_Vector *Rc, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Curvature2( Lgm_Vector *q, Lgm_Vector *Rc, int DerivScheme, double h, Lgm_MagModelInfo *m ); +int Lgm_Vdrift_GradB_Curv( Lgm_Vector *u0, Lgm_Vector *v_full, double Mass, double Charge, Lgm_Vector *v_GradB, Lgm_Vector *v_CurvB, Lgm_Vector *Rc_vec, Lgm_MagModelInfo *m ); + void quicksort_uli( unsigned long n, unsigned long *arr ); +/* + * Rotuines for computing the first invariant up to second order + */ +double Lgm_DoubleDot( double A[3][3], double B[3][3] ); +void Lgm_Set_Particle_Frames( Lgm_Vector *q, double Mass, double RestEnergy, double Energy, double Delta, double Phi, + Lgm_Vector *a, Lgm_Vector *c, Lgm_Vector *b, Lgm_Vector *v, Lgm_MagModelInfo *mInfo ); +void Lgm_Grad_Cb( Lgm_Vector *u0, double Grad_Cb[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Grad_b( Lgm_Vector *u0, double Grad_b[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Grad_GradB( Lgm_Vector *u0, double Grad_GradB[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dbdotvdcomp( Lgm_Vector *u0, Lgm_Vector *v, int comp, double *dbdotvdcomp, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dbdotvdx( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdx, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dbdotvdy( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdy, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_dbdotvdz( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdz, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Laplacian_bdotv( Lgm_Vector *u0, Lgm_Vector *v, double *Laplacian_bdotv, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Grad_bdotv( Lgm_Vector *u0, Lgm_Vector *v, Lgm_Vector *Grad_bdotv, int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Grad_Grad_bdotv( Lgm_Vector *u0, Lgm_Vector *v, double Grad_Grad_bdotv[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ); +void Lgm_Dyad( Lgm_Vector *a, Lgm_Vector *b, double r[3][3] ); +void Lgm_VectorDotTensor( Lgm_Vector *v, double T[3][3], Lgm_Vector *Result ); +void Lgm_TensorDotVector( double T[3][3], Lgm_Vector *v, Lgm_Vector *Result ); +void Lgm_TensorDotTensor( double A[3][3], double B[3][3], double R[3][3] ); +double Lgm_TraceTensor( double T[3][3] ); +void Lgm_TensorTranspose( double T[3][3], double T_transpose[3][3] ); +double Lgm_DoubleDot( double A[3][3], double B[3][3] ); +double Lgm_dBdr( Lgm_Vector *q, Lgm_MagModelInfo *mInfo ); +double Lgm_d2Bdr2( Lgm_Vector *q, Lgm_MagModelInfo *mInfo ); +double Lgm_Mu_Gardner( Lgm_Vector *q, Lgm_Vector *v, double Gamma, double Mass, double Charge, double *mu0_out, double *mu1_out, double *mu2_out, Lgm_MagModelInfo *mInfo ); +double Lgm_Mu_Burby( Lgm_Vector *q, Lgm_Vector *v, double Gamma, double Mass, double Charge, double *mu0_out, double *mu1_out, double *mu2_out, Lgm_MagModelInfo *mInfo ); + + /* * Misc Routines */ diff --git a/libLanlGeoMag/Lgm/Lgm_MaxwellJuttner.h b/libLanlGeoMag/Lgm/Lgm_MaxwellJuttner.h index 60dd2719c..2a6b469ab 100644 --- a/libLanlGeoMag/Lgm/Lgm_MaxwellJuttner.h +++ b/libLanlGeoMag/Lgm/Lgm_MaxwellJuttner.h @@ -11,6 +11,7 @@ #include double Lgm_MaxJut( double n, double T, double Ek, double E0 ); +void Lgm_MaxJut_Derivs( double n, double T, double Ek, double E0, double *dfdn, double *dfdT ); double Lgm_Maxwellian( double n, double T, double Ek, double E0 ); diff --git a/libLanlGeoMag/Lgm/Lgm_Misc.h b/libLanlGeoMag/Lgm/Lgm_Misc.h index 0b638ac63..1710b15cc 100644 --- a/libLanlGeoMag/Lgm/Lgm_Misc.h +++ b/libLanlGeoMag/Lgm/Lgm_Misc.h @@ -5,7 +5,8 @@ #include #include -void Lgm_ReplaceSubString( char *NewStr, char *OrigStr, char *SubStr, char *RepStr ); +void Lgm_ReplaceSubString2( char **OutStr, char *OrigStr, char *SubStr, char *RepStr ); +void Lgm_ReplaceSubString( char *OutStr, char *OrigStr, char *SubStr, char *RepStr ); #endif diff --git a/libLanlGeoMag/Lgm/Lgm_NrlMsise00.h b/libLanlGeoMag/Lgm/Lgm_NrlMsise00.h index b3578e36f..33bafd833 100644 --- a/libLanlGeoMag/Lgm/Lgm_NrlMsise00.h +++ b/libLanlGeoMag/Lgm/Lgm_NrlMsise00.h @@ -33,7 +33,7 @@ typedef struct Lgm_Msis00Info { double TN1[6], TN2[5], TN3[6], TGN1[3], TGN2[3], TGN3[3]; // PARMB COMMON BLOCK VARS - double GSURF, RE; + double GSURF, Rref; // CSW COMMON BLOCK VARS double *SW, ISW, *SWC; diff --git a/libLanlGeoMag/Lgm/Makefile.am b/libLanlGeoMag/Lgm/Makefile.am index 639dc499a..267d2b9ef 100644 --- a/libLanlGeoMag/Lgm/Makefile.am +++ b/libLanlGeoMag/Lgm/Makefile.am @@ -8,7 +8,7 @@ pkginclude_HEADERS = Lgm_CTrans.h Lgm_Eop.h Lgm_FieldIntInfo.h Lgm_IGRF.h Lgm_QinDenton.h Lgm_FastPowPoly.h Lgm_Misc.h Lgm_Constants.h Lgm_RBF.h uthash.h \ Lgm_HDF5.h Lgm_AE_index.h qsort.h Lgm_Tsyg2004.h Lgm_Utils.h Lgm_Tsyg2007.h Lgm_Metadata.h \ Lgm_Tsyg1996.h Lgm_Tsyg2001.h Lgm_KdTree.h Lgm_PriorityQueue.h Lgm_NrlMsise00.h Lgm_NrlMsise00_Data.h Lgm_Coulomb.h \ - Lgm_Objects.h Lgm_VelStepInfo.h Lgm_JPLeph.h Lgm_TabularBessel.h + Lgm_Objects.h Lgm_VelStepInfo.h Lgm_JPLeph.h Lgm_TabularBessel.h GPR.h diff --git a/libLanlGeoMag/Lgm/quicksort.h b/libLanlGeoMag/Lgm/quicksort.h index 4a7e94647..cf76a05b9 100644 --- a/libLanlGeoMag/Lgm/quicksort.h +++ b/libLanlGeoMag/Lgm/quicksort.h @@ -2,8 +2,11 @@ #define LGM_QUICKSORT_H void quicksort( unsigned long n, double *arr ); +void quicksort_uli( unsigned long n, unsigned long *arr ); void quicksort2( unsigned long n, double *arr, double *brr ); void quicksort2uli( unsigned long n, double *arr, unsigned long int *brr ); +void quicksort3( unsigned long n, double *arr, double *brr, double *crr ); void bubbleSort(unsigned long n, double *arr); + #endif diff --git a/libLanlGeoMag/Lgm_AE_index.c b/libLanlGeoMag/Lgm_AE_index.c old mode 100755 new mode 100644 diff --git a/libLanlGeoMag/Lgm_B_FromScatteredData.c b/libLanlGeoMag/Lgm_B_FromScatteredData.c index a08263c54..5617957d9 100644 --- a/libLanlGeoMag/Lgm_B_FromScatteredData.c +++ b/libLanlGeoMag/Lgm_B_FromScatteredData.c @@ -129,6 +129,7 @@ void Lgm_B_FromScatteredData_SetUp( Lgm_MagModelInfo *Info ) { Info->rbf_ht_alloced = FALSE; Info->RBF_nHashFinds = 0; Info->RBF_nHashAdds = 0; + } @@ -147,9 +148,11 @@ void Lgm_B_FromScatteredData_TearDown( Lgm_MagModelInfo *Info ) { if ( Info->Octree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->Octree_kNN ); + Info->Octree_kNN_Alloced = 0; } if ( Info->KdTree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + Info->KdTree_kNN_Alloced = 0; } } @@ -172,9 +175,11 @@ void Lgm_B_FromScatteredData4_TearDown( Lgm_MagModelInfo *Info ) { if ( Info->Octree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->Octree_kNN ); + Info->Octree_kNN_Alloced = 0; } if ( Info->KdTree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + Info->KdTree_kNN_Alloced = 0; } } @@ -235,9 +240,11 @@ void Lgm_B_FromScatteredData5_TearDown( Lgm_MagModelInfo *Info ) { if ( Info->Octree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->Octree_kNN ); + Info->Octree_kNN_Alloced = 0; } if ( Info->KdTree_kNN_Alloced > 0 ) { LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + Info->KdTree_kNN_Alloced = 0; } Info->RBF_CB.n = 0; @@ -485,7 +492,7 @@ int Lgm_B_FromScatteredData2( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In int Lgm_B_FromScatteredData3( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ) { int K, Kgot, n_data, i; - double eps, d; + double *eps, d; Lgm_KdTreeData *kNN; Lgm_DFI_RBF_Info *rbf; // single structure. Lgm_Vector *v_data, *B_data, B1, B2; @@ -596,6 +603,7 @@ int Lgm_B_FromScatteredData3( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In * repack data into arrays. This is wasteful also. */ n_data = Kgot; + LGM_ARRAY_1D( eps, n_data, double ); LGM_ARRAY_1D( I_data, n_data, unsigned long int ); LGM_ARRAY_1D( v_data, n_data, Lgm_Vector ); LGM_ARRAY_1D( B_data, n_data, Lgm_Vector ); @@ -611,6 +619,8 @@ int Lgm_B_FromScatteredData3( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In B_data[i].z = bbb[2]; I_data[i] = Info->KdTree_kNN[i].Id; + // eps not used yet for this version + eps[i] = Info->RBF_Eps; } QSORT( unsigned long int, I_data, n_data, int_lt ); @@ -647,6 +657,7 @@ Info->RBF_Eps = 1.0/(d2min); rbf = Lgm_DFI_RBF_Init( I_data, v_data, B_data, n_data, Info->RBF_Eps, Info->RBF_Type ); + LGM_ARRAY_1D_FREE( eps ); LGM_ARRAY_1D_FREE( I_data ); LGM_ARRAY_1D_FREE( v_data ); LGM_ARRAY_1D_FREE( B_data ); @@ -671,14 +682,14 @@ Info->RBF_Eps = 1.0/(d2min); * Evaluate Divergence Free Interpolation */ Lgm_DFI_RBF_Eval( v, &B1, rbf ); - //printf("Evaluating with rbf = %p at v = %g %g %g B1 = %g %g %g\n\n\n", rbf, v->x, v->y, v->z, B1.x, B1.y, B1.z); + //printf("Evaluating DFI_RBF with rbf = %p at v = %g %g %g B1 = %g %g %g\n\n\n", rbf, v->x, v->y, v->z, B1.x, B1.y, B1.z); /* * Evaluate derivatives of Divergence Free Interpolation * put in the Info structure. */ - Lgm_DFI_RBF_Derivs_Eval( v, &Info->RBF_dBdx, &Info->RBF_dBdy, &Info->RBF_dBdz, rbf ); if ( Info->RBF_CompGradAndCurl ) { + Lgm_DFI_RBF_Derivs_Eval( v, &Info->RBF_dBdx, &Info->RBF_dBdy, &Info->RBF_dBdz, rbf ); } @@ -722,8 +733,7 @@ Info->RBF_Eps = 1.0/(d2min); B->z = B1.z + B2.z; - //if ( Info->RBF_CompGradAndCurl ) { -{ + if ( Info->RBF_CompGradAndCurl ) { Lgm_Vector u, u0, Bvec; int DerivScheme, N; @@ -828,9 +838,6 @@ Info->RBF_Eps = 1.0/(d2min); dBdz.y += Info->RBF_dBdz.y; dBdz.z += Info->RBF_dBdz.z; /* -*/ -/* -*/ dBdx.x = Info->RBF_dBdx.x; dBdx.y = Info->RBF_dBdx.y; dBdx.z = Info->RBF_dBdx.z; @@ -842,6 +849,7 @@ dBdy.z = Info->RBF_dBdy.z; dBdz.x = Info->RBF_dBdz.x; dBdz.y = Info->RBF_dBdz.y; dBdz.z = Info->RBF_dBdz.z; +*/ // Compute final GradB b = *B; Lgm_NormalizeVector( &b ); @@ -937,7 +945,6 @@ int Lgm_B_FromScatteredData4( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In double q[3]; q[0] = v->x; q[1] = v->y; q[2] = v->z; Lgm_KdTree_kNN( q, 3, Info->KdTree, K, &Kgot, Info->KdTree_kNN_MaxDist2, Info->KdTree_kNN ); - //printf("K, Kgot = %d %d q = %g %g %g Info->KdTree_kNN_MaxDist2 = %g \n", K, Kgot, q[0], q[1], q[2], Info->KdTree_kNN_MaxDist2 ); // not needed? Put under verbosity setting? @@ -975,6 +982,9 @@ int Lgm_B_FromScatteredData4( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *In ++(Info->RBF_nHashFinds); +//if (0==1){ +//} +//rbf = NULL; /* @@ -1075,8 +1085,8 @@ Info->RBF_Eps = 1.0/(d2min*10.0); * Evaluate derivatives of Divergence Free Interpolation * put in the Info structure. */ - Lgm_Vec_RBF_Derivs_Eval( v, &Info->RBF_dBdx, &Info->RBF_dBdy, &Info->RBF_dBdz, rbf ); if ( Info->RBF_CompGradAndCurl ) { + Lgm_Vec_RBF_Derivs_Eval( v, &Info->RBF_dBdx, &Info->RBF_dBdy, &Info->RBF_dBdz, rbf ); } @@ -2194,3 +2204,457 @@ double EstimateGridRes( Lgm_Vector *v ){ +/** This routine is the same as Lgm_B_FromScatteredData4(), except that here we + * use the DFI RBF funcs. Experimental... Testing.... + * + * \param[in] v - array of position vectors + * \param[in] B - array of B-field vectors at the corresponding v's + * \param[in,out] Info - Pointer to Lgm_MagModelInfo structure. + * + * \return Always returns 1. Fix this...? + * + * \author M. G. Henderson + * \date March 8, 2019 + * + * + */ +int Lgm_B_FromScatteredData6( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ) { + + int K, Kgot, n_data, i; + double *eps, d; + Lgm_KdTreeData *kNN; + Lgm_DFI_RBF_Info *rbf; // single structure. + Lgm_Vector *v_data, *B_data, B1, B2; + Lgm_Vector b, Grad_B1, GradB_dipole; + unsigned long int *I_data; + unsigned long int *LookUpKey; // key comprised of an array of unsigned long int Id's + int KeyLength; // length (in bytes) of LookUpKey + int (*Dipole)(); // tmp Pointer to Bfield function + + + /* + * Make sure KdTree has been initialized + */ + if ( Lgm_Magnitude( v ) > 1.5 ) { + + /* + * Allocate space for the K Nearest Neighbors. + * This is probably a bit wasteful... + * Should cache this array. + */ + K = Info->KdTree_kNN_k; + if (K > 2) { + + if ( Info->KdTree_kNN_Alloced == 0 ) { + + LGM_ARRAY_1D( Info->KdTree_kNN, K, Lgm_KdTreeData ); + Info->KdTree_kNN_Alloced = K; + + } else if ( K != Info->KdTree_kNN_Alloced ) { + + /* + * kNN is allocated but K has changed. Realloc. + */ + LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + LGM_ARRAY_1D( Info->KdTree_kNN, K, Lgm_KdTreeData ); + + } + Info->KdTree_kNN_Alloced = K; + + } else { + + printf("Lgm_B_FromScatteredData4(): Error. Not enough nearest neighbors specified: Info->KdTree_kNN_k = %d\n", Info->KdTree_kNN_k); + exit(-1); + + } + + + /* + * Find the K Nearest Neighbors. + */ + double q[3]; + q[0] = v->x; q[1] = v->y; q[2] = v->z; + Lgm_KdTree_kNN( q, 3, Info->KdTree, K, &Kgot, Info->KdTree_kNN_MaxDist2, Info->KdTree_kNN ); + + + // not needed? Put under verbosity setting? + for ( i=0; iKdTree_kNN[i].Dist2 > Info->KdTree_kNN_MaxDist2){ + printf("Lgm_B_FromScatteredData4(): ERROR - Info->KdTree_kNN[i].Dist2 = %g\n", Info->KdTree_kNN[i].Dist2); + } + } + + + /* + * From the K nearest neighbors, construct a key for the hash-table. + * Probably should sort them so that different permutations of the same k + * NN's will be identified as the same set. But lets worry about that + * later. + * + */ + LGM_ARRAY_1D( LookUpKey, Kgot, unsigned long int ); + for ( i=0; iKdTree_kNN[i].Id; + KeyLength = Kgot*sizeof( unsigned long int ); + QSORT( unsigned long int, LookUpKey, Kgot, int_lt ); + //quicksort_uli( (long int)Kgot, LookUpKey-1 ); + + + + /* + * Look up the key in the hash-table to see if its already there. + * If it exists, we bypass refitting the RBF weights. + */ + //printf("Searching for: " ); + //for(i=0;irbf_ht, LookUpKey, KeyLength, rbf ); + ++(Info->RBF_nHashFinds); + + +//if (0==1){ +//} +//rbf = NULL; + + + /* + * If key didnt exist in hash-table, we need to compute RBF weights, package up info + * into a structure and add it to the hash table. + */ + if ( rbf == NULL ) { + + + + //printf("Did not find key - computing new rbf coeffs\n\n"); + + /* + * repack data into arrays. This is wasteful also. + */ + n_data = Kgot; + LGM_ARRAY_1D( eps, n_data, double ); + LGM_ARRAY_1D( I_data, n_data, unsigned long int ); + LGM_ARRAY_1D( v_data, n_data, Lgm_Vector ); + LGM_ARRAY_1D( B_data, n_data, Lgm_Vector ); + double *bbb; + for ( i=0; iKdTree_kNN[i].Position[0]; + v_data[i].y = Info->KdTree_kNN[i].Position[1]; + v_data[i].z = Info->KdTree_kNN[i].Position[2]; + + bbb = (double *)Info->KdTree_kNN[i].Object; + B_data[i].x = bbb[0]; + B_data[i].y = bbb[1]; + B_data[i].z = bbb[2]; + + I_data[i] = Info->KdTree_kNN[i].Id; + eps[i] = Info->RBF_Eps; + } + QSORT( unsigned long int, I_data, n_data, int_lt ); + +/* +double dx, dy, dz, d2, d2min; +int j; +d2min = 1e6; +for ( i=0; i 0.0) && ( d2 < d2min) ) { + d2min = d2; + } + + } + } +} +//printf("d2min = %g\n", d2min); +Info->RBF_Eps = 1.0/(d2min*10.0); +*/ + + + /* + * Construct the rbf structure. We dont free these until the hash + * table is done with. Note that the hash table will be the only + * reference to the pointer. To free, use + * Lgm_B_FromScatteredData_TearDown(). + */ + rbf = Lgm_DFI_RBF_Init( I_data, v_data, B_data, n_data, Info->RBF_Eps, Info->RBF_Type ); + + + LGM_ARRAY_1D_FREE( eps ); + LGM_ARRAY_1D_FREE( I_data ); + LGM_ARRAY_1D_FREE( v_data ); + LGM_ARRAY_1D_FREE( B_data ); + + //printf("Adding item to hash table\n"); + HASH_ADD_KEYPTR( hh, Info->rbf_ht, rbf->LookUpKey, KeyLength, rbf ); + ++(Info->RBF_nHashAdds); + + } else { + + //printf("got one\n"); + //printf(" Found: " ); + //for(i=0;iLookUpKey[i] ); + //printf("\n\n"); + + } + + + + + /* + * Evaluate Divergence Free Interpolation + */ + Lgm_DFI_RBF_Eval( v, &B1, rbf ); + //printf("Evaluating with rbf = %p at v = %g %g %g B1 = %g %g %g\n\n\n", rbf, v->x, v->y, v->z, B1.x, B1.y, B1.z); + + /* + * Evaluate derivatives of Divergence Free Interpolation + * put in the Info structure. + */ + if ( Info->RBF_CompGradAndCurl ) { + Lgm_DFI_RBF_Derivs_Eval( v, &Info->RBF_dBdx, &Info->RBF_dBdy, &Info->RBF_dBdz, rbf ); + } + + + /* + * Cleanup. Free rbf, kNN, etc.. + */ + LGM_ARRAY_1D_FREE( LookUpKey ); + + } else { + + B1.x = B1.y = B1.z = 0.0; + + } + + + + // Save Bfield, so we can temporaily swap it for an internal model (so we can compute GradB) + switch ( Info->InternalModel ){ + + case LGM_CDIP: + Lgm_B_cdip( v, &B2, Info ); + Dipole = Lgm_B_cdip; + break; + case LGM_EDIP: + Lgm_B_edip( v, &B2, Info ); + Dipole = Lgm_B_edip; + break; + case LGM_IGRF: + Lgm_B_igrf( v, &B2, Info ); + Dipole = Lgm_B_igrf; + break; + default: + fprintf(stderr, "Lgm_B_FromScatteredData4(): Unknown internal model (%d)\n", Info->InternalModel ); + break; + + } + + // Total B + B->x = B1.x + B2.x; + B->y = B1.y + B2.y; + B->z = B1.z + B2.z; +//B->x = B1.x; +//B->y = B1.y; +//B->z = B1.z; + + + //if ( Info->RBF_CompGradAndCurl ) { +{ + + Lgm_Vector u, u0, Bvec; + int DerivScheme, N; + Lgm_Vector dBdx, dBdy, dBdz; + double f1x[7], f2x[7], f3x[7]; + double f1y[7], f2y[7], f3y[7]; + double f1z[7], f2z[7], f3z[7]; + double H, Bmag; + double h = 1e-3; + + u0 = *v; + N = 1; DerivScheme = LGM_DERIV_TWO_POINT; + + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = u0; H = (double)i*h; u.x += H; + Dipole( &u, &Bvec, Info ); + f1x[i+N] = Bvec.x; + f2x[i+N] = Bvec.y; + f3x[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = u0; H = (double)i*h; u.y += H; + Dipole( &u, &Bvec, Info ); + f1y[i+N] = Bvec.x; + f2y[i+N] = Bvec.y; + f3y[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = u0; H = (double)i*h; u.z += H; + Dipole( &u, &Bvec, Info ); + Bmag = Lgm_Magnitude( &Bvec ); + f1z[i+N] = Bvec.x; + f2z[i+N] = Bvec.y; + f3z[i+N] = Bvec.z; + } + } + if (DerivScheme == LGM_DERIV_SIX_POINT){ + + dBdx.x = (f1x[6] - 9.0*f1x[5] + 45.0*f1x[4] - 45.0*f1x[2] + 9.0*f1x[1] - f1x[0])/(60.0*h); + dBdx.y = (f2x[6] - 9.0*f2x[5] + 45.0*f2x[4] - 45.0*f2x[2] + 9.0*f2x[1] - f2x[0])/(60.0*h); + dBdx.z = (f3x[6] - 9.0*f3x[5] + 45.0*f3x[4] - 45.0*f3x[2] + 9.0*f3x[1] - f3x[0])/(60.0*h); + + dBdy.x = (f1y[6] - 9.0*f1y[5] + 45.0*f1y[4] - 45.0*f1y[2] + 9.0*f1y[1] - f1y[0])/(60.0*h); + dBdy.y = (f2y[6] - 9.0*f2y[5] + 45.0*f2y[4] - 45.0*f2y[2] + 9.0*f2y[1] - f2y[0])/(60.0*h); + dBdy.z = (f3y[6] - 9.0*f3y[5] + 45.0*f3y[4] - 45.0*f3y[2] + 9.0*f3y[1] - f3y[0])/(60.0*h); + + dBdz.x = (f1z[6] - 9.0*f1z[5] + 45.0*f1z[4] - 45.0*f1z[2] + 9.0*f1z[1] - f1z[0])/(60.0*h); + dBdz.y = (f2z[6] - 9.0*f2z[5] + 45.0*f2z[4] - 45.0*f2z[2] + 9.0*f2z[1] - f2z[0])/(60.0*h); + dBdz.z = (f3z[6] - 9.0*f3z[5] + 45.0*f3z[4] - 45.0*f3z[2] + 9.0*f3z[1] - f3z[0])/(60.0*h); + + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + + dBdx.x = (-f1x[4] + 8.0*f1x[3] - 8.0*f1x[1] + f1x[0])/(12.0*h); + dBdx.y = (-f2x[4] + 8.0*f2x[3] - 8.0*f2x[1] + f2x[0])/(12.0*h); + dBdx.z = (-f3x[4] + 8.0*f3x[3] - 8.0*f3x[1] + f3x[0])/(12.0*h); + + dBdy.x = (-f1y[4] + 8.0*f1y[3] - 8.0*f1y[1] + f1y[0])/(12.0*h); + dBdy.y = (-f2y[4] + 8.0*f2y[3] - 8.0*f2y[1] + f2y[0])/(12.0*h); + dBdy.z = (-f3y[4] + 8.0*f3y[3] - 8.0*f3y[1] + f3y[0])/(12.0*h); + + dBdz.x = (-f1z[4] + 8.0*f1z[3] - 8.0*f1z[1] + f1z[0])/(12.0*h); + dBdz.y = (-f2z[4] + 8.0*f2z[3] - 8.0*f2z[1] + f2z[0])/(12.0*h); + dBdz.z = (-f3z[4] + 8.0*f3z[3] - 8.0*f3z[1] + f3z[0])/(12.0*h); + + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + + dBdx.x = (f1x[2] - f1x[0])/(2.0*h); + dBdx.y = (f2x[2] - f2x[0])/(2.0*h); + dBdx.z = (f3x[2] - f3x[0])/(2.0*h); + + dBdy.x = (f1y[2] - f1y[0])/(2.0*h); + dBdy.y = (f2y[2] - f2y[0])/(2.0*h); + dBdy.z = (f3y[2] - f3y[0])/(2.0*h); + + dBdz.x = (f1z[2] - f1z[0])/(2.0*h); + dBdz.y = (f2z[2] - f2z[0])/(2.0*h); + dBdz.z = (f3z[2] - f3z[0])/(2.0*h); + + } else { + printf("huh?\n"); + exit(0); + } + + + + + // Add in the contribution from external field. + dBdx.x += Info->RBF_dBdx.x; + dBdx.y += Info->RBF_dBdx.y; + dBdx.z += Info->RBF_dBdx.z; + + dBdy.x += Info->RBF_dBdy.x; + dBdy.y += Info->RBF_dBdy.y; + dBdy.z += Info->RBF_dBdy.z; + + dBdz.x += Info->RBF_dBdz.x; + dBdz.y += Info->RBF_dBdz.y; + dBdz.z += Info->RBF_dBdz.z; +/* +*/ +/* +dBdx.x = Info->RBF_dBdx.x; +dBdx.y = Info->RBF_dBdx.y; +dBdx.z = Info->RBF_dBdx.z; + +dBdy.x = Info->RBF_dBdy.x; +dBdy.y = Info->RBF_dBdy.y; +dBdy.z = Info->RBF_dBdy.z; + +dBdz.x = Info->RBF_dBdz.x; +dBdz.y = Info->RBF_dBdz.y; +dBdz.z = Info->RBF_dBdz.z; +*/ + + // Compute final GradB + b = *B; Lgm_NormalizeVector( &b ); + Info->RBF_Grad_B.x = Lgm_DotProduct( &b, &dBdx ); + Info->RBF_Grad_B.y = Lgm_DotProduct( &b, &dBdy ); + Info->RBF_Grad_B.z = Lgm_DotProduct( &b, &dBdz ); + + // Compute Curl_B + Info->RBF_Curl_B.x = Info->RBF_dBdy.z - Info->RBF_dBdz.y; + Info->RBF_Curl_B.y = Info->RBF_dBdz.x - Info->RBF_dBdx.z; + Info->RBF_Curl_B.z = Info->RBF_dBdx.y - Info->RBF_dBdy.x; + } + + + + return( 1 ); + +} +/* + * Setup the hash table used in Lgm_B_FromScatteredData6(). + */ +void Lgm_B_FromScatteredData6_SetUp( Lgm_MagModelInfo *Info ) { + + +Lgm_DFI_RBF_Info ***b; + + if ( Info->rbf_ht_alloced ) Lgm_B_FromScatteredData6_TearDown( Info ); + Info->rbf_ht = NULL; + Info->vec_rbf_e_ht = NULL; + Info->rbf_ht_alloced = FALSE; + Info->RBF_nHashFinds = 0; + Info->RBF_nHashAdds = 0; + + + Info->RBF_CB.n = 0; + Info->RBF_CB.nEntries = 0; // Initial entries + Info->RBF_CB.N = 200000; // Max entries + Info->RBF_CB.Buf1 = (Lgm_Vec_RBF_Info **)calloc( Info->RBF_CB.N, sizeof( Lgm_Vec_RBF_Info *) ); + Info->RBF_CB.Buf2 = (Lgm_Vec_RBF_Info **)calloc( Info->RBF_CB.N, sizeof( Lgm_Vec_RBF_Info *) ); + + Info->dfi_rbf_ht_size = 0.0; // Initial size in MB + Info->dfi_rbf_ht_maxsize = 2000.0; // Max size in MB + Info->vec_rbf_ht_size = 0.0; // Initial size in MB + Info->vec_rbf_ht_maxsize = 2000.0; // Max size in MB +// Info->vec_rbf_ht_maxsize = 200.0; // Max size in MB + Info->RBF_CB.oldest_i = 0; + Info->RBF_CB.newest_i = -1; + + +} +/* + * Iterates over all the entries in the hash table and 1) deletes them from + * the hash table, then 2) free the structure itself. + * + * Really should unify the rbf structures.. + */ +void Lgm_B_FromScatteredData6_TearDown( Lgm_MagModelInfo *Info ) { + + Lgm_DFI_RBF_Info *rbf, *rbf_tmp; + + HASH_ITER( hh, Info->rbf_ht, rbf, rbf_tmp ) { + HASH_DELETE( hh, Info->rbf_ht, rbf ); + Lgm_DFI_RBF_Free( rbf ); + } + + if ( Info->Octree_kNN_Alloced > 0 ) { + LGM_ARRAY_1D_FREE( Info->Octree_kNN ); + Info->Octree_kNN_Alloced = 0; + } + if ( Info->KdTree_kNN_Alloced > 0 ) { + LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + Info->KdTree_kNN_Alloced = 0; + } + +} diff --git a/libLanlGeoMag/Lgm_CTrans.c b/libLanlGeoMag/Lgm_CTrans.c index 7a0458d4e..3a15b89d3 100644 --- a/libLanlGeoMag/Lgm_CTrans.c +++ b/libLanlGeoMag/Lgm_CTrans.c @@ -392,17 +392,17 @@ void Lgm_Set_Coord_Transforms( long int date, double UTC, Lgm_CTrans *c ) { double RA, DEC; double gclat, glon, psi; double g[14][14], h[14][14], Tmp[3][3]; - double varep90, varpi90; - double Zeta, Theta, Zee; - double SinZee, CosZee, SinZeta, CosZeta, SinTheta, CosTheta; - Lgm_Vector S, K, Y, Z, D, Dmod, Dgsm, u_mod, u_tod, u_gei, Zgeo, X; - double RA_tod, DEC_tod; - double RA_gei, DEC_gei; + double varep90, varpi90; + double Zeta, Theta, Zee; + double SinZee, CosZee, SinZeta, CosZeta, SinTheta, CosTheta; + Lgm_Vector S, K, Y, Z, D, Dmod, Dgsm, u_mod, u_tod, u_gei, Zgeo, X; + double RA_tod, DEC_tod; + double RA_gei, DEC_gei; double sxp, cxp, syp, cyp; - double sdp, cdp, se, ce, set, cet; - double T_UT1, T2_UT1, T3_UT1, T_TT, T2_TT, T3_TT, T4_TT, T5_TT; - double cos_epsilon, sin_epsilon, sin_lambnew, sin_b, cos_b, sin_l, tmp; - int i, j, N; + double sdp, cdp, se, ce, set, cet; + double T_UT1, T2_UT1, T3_UT1, T_TT, T2_TT, T3_TT, T4_TT, T5_TT; + double cos_epsilon, sin_epsilon, sin_lambnew, sin_b, cos_b, sin_l, tmp; + int i, j, N, sgn; /* @@ -419,7 +419,8 @@ void Lgm_Set_Coord_Transforms( long int date, double UTC, Lgm_CTrans *c ) { c->UTC.Dow = Lgm_DayOfWeek( year, month, day, c->UTC.DowStr ); c->UTC.JD = Lgm_JD( c->UTC.Year, c->UTC.Month, c->UTC.Day, c->UTC.Time, LGM_TIME_SYS_UTC, c ); c->UTC.T = (c->UTC.JD - 2451545.0)/36525.0; - c->UTC.fYear = (double)c->UTC.Year + ((double)c->UTC.Doy - 1.0 + c->UTC.Time/24.0)/(365.0 + (double)Lgm_LeapYear(c->UTC.Year)); + c->UTC.fYear = (double)c->UTC.Year + ((double)c->UTC.Doy - 1.0 + c->UTC.Time/24.0)/(365.0 + (double)Lgm_LeapYear(c->UTC.Year)); + Lgm_UT_to_HMSd( UTC, &sgn, &(c->UTC.Hour), &(c->UTC.Minute), &(c->UTC.Second) ); // set DAT c->DAT = Lgm_GetLeapSeconds( c->UTC.JD, c ); @@ -2340,3 +2341,122 @@ char *Lgm_StrToUpper( char *str, int nmax ) { } return( str ); } + +/* + * Compute the Geocentric Geographic Latitude/Longitude of the Day/Night Terminator. + * + * Uses the following formula: + * + * sin(alpha) = sin(delta) sin(phi) + cos(delta)cos(phi)cos(omega) + * + * where: + * + * alpha = elevation angle of Sun. (sunset/rise = 0, civil twilight = -6, nautical twilight = -12, astronominal twilight = -18) + * delta = declinatioon of Sun + * phi = latitude of observer + * omega = local hour angle = L + GST - RA + * L = longitude of observer + * GST = Greenwhich Sidereal Time + * RA = Right Ascention of Sun + * + * To compute, we give it GLAT and solve for GLON. To solve for GLON, + * substitue sin(phi) = sqrt(1-cos(phi)^2). The we have; + * + * sin(alpha) = sin(delta)sqrt(1-cos(phi)^2) + cos(delta)cos(phi)cos(omega) + * sin(delta)sqrt(1-cos(phi)^2) = sin(alpha) - cos(delta)cos(phi)cos(omega) + * + * Let u = cos(phi), then squaring both sides: + * + * sin^2(delta)(1-u^2) = sin^2(alpha) - 2sin(alpha)cos(delta)cos(omega) u + cos^2(delta) cos^2(omega) u^2 + * + * sin^2(alpha) - sin^2(delta) + sin^2(delta) u^2 - 2sin(alpha)cos(delta)cos(omega) u + cos^2(delta) cos^2(omega) u^2 = 0 + * {sin^2(alpha) - sin^2(delta)} - {2sin(alpha)cos(delta)cos(omega)} u + {sin^2(delta) + cos^2(delta) cos^2(omega)} u^2 = 0 + * + * + * Let A = {sin^2(delta) + cos^2(delta) cos^2(omega)} + * B = {-2sin(alpha)cos(delta)cos(omega)} + * C = {sin^2(alpha) - sin^2(delta)} + * + * A u^2 + B u + C = 0 + * + * Solve for u with Quad Formula. + * + */ +void Lgm_Terminator( double GLON, double *GLAT, int *nRoots, double alpha, Lgm_CTrans *c ) { + + double sd, cd, sa, co, sd2, A, B, C, A2; + double disc, s, u1, u2, phi1, phi2, phi3, phi4; + double delta, omega, L, GST, RA; + + omega = (GLON - c->RA_sun + c->gmst*15.0)*RadPerDeg; + delta = c->DEC_sun*RadPerDeg; + + + sd = sin(delta); cd = cos(delta); + sa = sin(alpha*RadPerDeg); + co = cos(omega); + + sd2 = sd*sd; + + A = sd2 + cd*cd * co*co; A2 = 2.0*A; + B = -2.0*sa*cd*co; + C = sa*sa - sd2; + + disc = B*B - 4.0*A*C; + if ( disc >= 0.0 ){ + s = sqrt( disc ); + u1 = (-B + s)/A2; + u2 = (-B - s)/A2; + } + + /* + * We cannot get the right value of phi from just cos(phi) alone. We also need sin(phi). + * sin(alpha) = sin(delta) sin(phi) + cos(delta)cos(phi)cos(omega) + */ + double SinPhi; + SinPhi = (sa - cd*u1*co)/sd; + phi1 = (fabs(SinPhi) <= 1.0) ? DegPerRad*atan2( SinPhi, u1 ) : LGM_FILL_VALUE; + SinPhi = (sa - cd*u2*co)/sd; + phi2 = (fabs(SinPhi) <= 1.0) ? DegPerRad*atan2( SinPhi, u2 ) : LGM_FILL_VALUE; + + if ( (phi1 > 90.0) || (phi1 < -90.0) ) { phi1 = LGM_FILL_VALUE; } + if ( (phi2 > 90.0) || (phi2 < -90.0) ) { phi2 = LGM_FILL_VALUE; } + + //printf("A. SinPhi = %g CosPhi = %g B, disc, A2, s = %g %g %g %g u1, u2 = %g %g phi1, phi2 = %lf %lf\n", SinPhi, u1, B, disc, A2, s, u1, u2, phi1, phi2); + + + + /* + * return min sort order + */ + if ( (phi1 > -1e20) && (phi2 > -1e20) ) { + // Two valid roots. + *nRoots = 2; + if ( phi1 < phi2 ) { + GLAT[0] = phi1; + GLAT[1] = phi2; + } else { + GLAT[0] = phi2; + GLAT[1] = phi1; + } + } else if ( (phi1 > -1e20) ) { + // only phi1 is a valid root + *nRoots = 1; + GLAT[0] = phi1; + GLAT[1] = LGM_FILL_VALUE; + } else if ( (phi2 > -1e20) ) { + // only phi2 is a valid root + *nRoots = 1; + GLAT[0] = phi2; + GLAT[1] = LGM_FILL_VALUE; + } else { + // no valid roots + *nRoots = 0; + GLAT[0] = LGM_FILL_VALUE; + GLAT[1] = LGM_FILL_VALUE; + } + + + return; + +} diff --git a/libLanlGeoMag/Lgm_ComputeLstarVersusPA.c b/libLanlGeoMag/Lgm_ComputeLstarVersusPA.c index 9e321f0e8..fe131f77c 100644 --- a/libLanlGeoMag/Lgm_ComputeLstarVersusPA.c +++ b/libLanlGeoMag/Lgm_ComputeLstarVersusPA.c @@ -51,6 +51,7 @@ void Lgm_ComputeLstarVersusPA( long int Date, double UTC, Lgm_Vector *u, int nAl double sa, sa2, Blocal; double Lam, CosLam, LSimple; int i, k, LS_Flag, nn, tk, TraceFlag; + int nShabII, nShabI; char *PreStr, *PostStr; LstarInfo = MagEphemInfo->LstarInfo; @@ -149,7 +150,7 @@ void Lgm_ComputeLstarVersusPA( long int Date, double UTC, Lgm_Vector *u, int nAl * set private here -- the threads must not interfere with each other. */ #if USE_OPENMP - #pragma omp parallel private(LstarInfo2,LstarInfo3,sa,sa2,LS_Flag,nn,tk,PreStr,PostStr) + #pragma omp parallel private(LstarInfo2,LstarInfo3,sa,sa2,LS_Flag,nn,tk,PreStr,PostStr,nShabII,nShabI) #pragma omp for schedule(dynamic, 1) #endif for ( i=0; inAlpha; i++ ){ // LOOP OVER PITCH ANGLES @@ -202,19 +203,48 @@ void Lgm_ComputeLstarVersusPA( long int Date, double UTC, Lgm_Vector *u, int nAl MagEphemInfo->I[i] = LstarInfo2->I[0]; // I[0] is I for the FL that the sat is on. MagEphemInfo->K[i] = LstarInfo2->I[0]*sqrt(MagEphemInfo->Bm[i]*1e-5); // Second invariant MagEphemInfo->Sb[i] = LstarInfo2->SbIntegral0; // SbIntegral0 is Sb for the FL that the sat is on. + + /* * Determine the type of the orbit */ + nShabII = nShabI = 0; if ( LS_Flag >= 0 ) { + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_CLOSED; - for ( k=0; knMinMax; ++k ) { - if ( LstarInfo2->nMinima[k] > 1 ) LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_CLOSED_SHABANSKY; + for ( nn=0; nnnPnts; ++nn ) { + if ( LstarInfo2->nMinima[nn] > 1 ) { + if ( LstarInfo2->nBounceRegions[nn] > 1 ) { + nShabII++; + } else { + nShabI++; + } + } } + if (nShabII > 0) { + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_CLOSED_SHABANSKY_II; + } else if (nShabI > 0) { + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_CLOSED_SHABANSKY_I; + } + } else { - LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN; - for ( k=0; knMinMax; ++k ) { - if ( LstarInfo2->nMinima[k] > 1 ) LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY; + + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_CLOSED; + for ( nn=0; nnnPnts; ++nn ) { + if ( LstarInfo2->nMinima[nn] > 1 ) { + if ( LstarInfo2->nBounceRegions[nn] > 1 ) { + nShabII++; + } else { + nShabI++; + } + } + } + if (nShabII > 0) { + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY_II; + } else if (nShabI > 0) { + LstarInfo2->DriftOrbitType = LGM_DRIFT_ORBIT_OPEN_SHABANSKY_I; } + } MagEphemInfo->DriftOrbitType[i] = LstarInfo2->DriftOrbitType; @@ -232,12 +262,18 @@ void Lgm_ComputeLstarVersusPA( long int Date, double UTC, Lgm_Vector *u, int nAl */ MagEphemInfo->nShellPoints[i] = LstarInfo2->nPnts; for (nn=0; nnnPnts; nn++ ){ + + // This Pmin does not seem to be the right one when we have Shabansky MagEphemInfo->Shell_Pmin[i][nn] = LstarInfo2->Pmin[nn]; + MagEphemInfo->Shell_Bmin[i][nn] = LstarInfo2->Bmin[nn]; MagEphemInfo->Shell_GradI[i][nn] = LstarInfo2->GradI[nn]; MagEphemInfo->Shell_Vgc[i][nn] = LstarInfo2->Vgc[nn]; - MagEphemInfo->ShellI[i][nn] = LstarInfo2->I[nn]; + +// This does really capture the I/2 bit? +MagEphemInfo->ShellI[i][nn] = LstarInfo2->I[nn]; +MagEphemInfo->nBounceRegions[i][nn] = LstarInfo2->nBounceRegions[nn]; MagEphemInfo->ShellSphericalFootprint_Pn[i][nn] = LstarInfo2->Spherical_Footprint_Pn[nn]; MagEphemInfo->ShellSphericalFootprint_Sn[i][nn] = LstarInfo2->Spherical_Footprint_Sn[nn]; diff --git a/libLanlGeoMag/Lgm_DFI_RBF.c b/libLanlGeoMag/Lgm_DFI_RBF.c index 10e25ddb6..1fa9c4858 100644 --- a/libLanlGeoMag/Lgm_DFI_RBF.c +++ b/libLanlGeoMag/Lgm_DFI_RBF.c @@ -1,3 +1,4 @@ +#include /*! \file Lgm_DFI_RBF.c * \brief Routines to perform Divergence-Free-Interpolation of vector field data (for example, B defined on meshes). * @@ -229,6 +230,7 @@ #include "Lgm/Lgm_RBF.h" +//#define LGM_DFI_RBF_SOLVER LGM_SVD //#define LGM_DFI_RBF_SOLVER LGM_CHOLESKY_DECOMP #define LGM_DFI_RBF_SOLVER LGM_PLU_DECOMP @@ -623,18 +625,23 @@ Lgm_DFI_RBF_Info *Lgm_DFI_RBF_Init( unsigned long int *I_data, Lgm_Vector *v, Lg rbf->LookUpKey[i] = I_data[i]; rbf->v[i] = v[i]; } - // This subtraction doesntm seem to work out very well... -// rbf->Bx0 = B[0].x; -// rbf->By0 = B[0].y; -// rbf->Bz0 = B[0].z; + // This subtraction doesnt seem to work out very well... + rbf->Bx0 = B[0].x; + rbf->By0 = B[0].y; + rbf->Bz0 = B[0].z; double Bbkg; for ( Bbkg = 0.0, i=0; iBx0 = Bbkg/(double)n; for ( Bbkg = 0.0, i=0; iBy0 = Bbkg/(double)n; for ( Bbkg = 0.0, i=0; iBz0 = Bbkg/(double)n; - rbf->Bx0 = 0.0; - rbf->By0 = 0.0; - rbf->Bz0 = 0.0; +/* +*/ +// rbf->Bx0 = B[0].x; +// rbf->By0 = B[0].y; +// rbf->Bz0 = B[0].z; +//rbf->Bx0 = 0.0; +//rbf->By0 = 0.0; +//rbf->Bz0 = 0.0; /* * Fill d array. (Subtract off the field at the nearest point v[0] -- See @@ -660,6 +667,7 @@ for ( Bbkg = 0.0, i=0; iBz0 = Bbkg/(double)n; // Get Phi( v_i - v_j ) Lgm_DFI_RBF_Phi( &v[i], &v[j], Phi, rbf ); + //printf("v%d = %g %g %g v%d = %g %g %g Phi = %g %g %g %g %g %g %g %g %g\n", i, v[i].x, v[i].y, v[i].z, j, v[j].x, v[j].y, v[j].z, Phi[0][0], Phi[0][1], Phi[0][2], Phi[1][0], Phi[1][1], Phi[1][2], Phi[2][0], Phi[2][1], Phi[2][2]); for ( p=0; p<3; p++ ){ // subarray row for ( q=0; q<3; q++ ){ // subarray column @@ -682,9 +690,28 @@ for ( Bbkg = 0.0, i=0; iBz0 = Bbkg/(double)n; } printf("\n"); } - */ + printf("\n\n"); +gsl_vector_complex *eval = gsl_vector_complex_alloc (n3); +gsl_matrix_complex *evec = gsl_matrix_complex_alloc (n3, n3); + +gsl_eigen_nonsymmv_workspace *w = gsl_eigen_nonsymmv_alloc (n3); +gsl_eigen_nonsymmv( A, eval, evec, w ); +gsl_eigen_nonsymmv_free (w); + +for (i=0; iz; //km Psc_mag = Lgm_Magnitude( &Psc ); // km + // vector pointing from sc to earth + Pe.x = -Psc.x; + Pe.y = -Psc.y; + Pe.z = -Psc.z; + /* * We need (all in MOD); * Rsun - Earth to Sun Vector. @@ -46,6 +51,7 @@ int Lgm_EarthEclipse( Lgm_Vector *u, Lgm_CTrans *c ) { Rsun = c->Sun; Lgm_ScaleVector( &Rsun, Rsun_mag ); // km + // vector pointing from sc to sun Psun.x = Rsun.x - Psc.x; //km Psun.y = Rsun.y - Psc.y; //km Psun.z = Rsun.z - Psc.z; //km @@ -60,16 +66,16 @@ int Lgm_EarthEclipse( Lgm_Vector *u, Lgm_CTrans *c ) { /* * Compute angle between Psun and Psc */ - Theta = acos( Lgm_DotProduct( &Psun, &Psc )/(Psun_mag*Psc_mag) ); + Theta = acos( Lgm_DotProduct( &Psun, &Pe )/(Psun_mag*Psc_mag) ); /* * Compute anglular radius of Earth and Sun as seen at S/C */ - //printf("Re/Psc_mag, Re, Psc_mag = %g %g %g\n", Re/Psc_mag, Re, Psc_mag ); + printf("Re/Psc_mag, Re, Psc_mag = %g %g %g\n", Re/Psc_mag, Re, Psc_mag ); ThetaE = asin( Re/Psc_mag ); ThetaS = asin( SOLAR_RADIUS/Psun_mag ); - //printf("Theta, ThetaE, ThetaS = %g %g %g\n", Theta, ThetaE, ThetaS ); + printf("Theta, ThetaE, ThetaS = %g %g %g\n", Theta, ThetaE, ThetaS ); /* @@ -82,14 +88,17 @@ int Lgm_EarthEclipse( Lgm_Vector *u, Lgm_CTrans *c ) { if ( ( ThetaE > ThetaS ) && ( Theta < (ThetaE - ThetaS) ) ){ Type = LGM_UMBRAL_ECLIPSE; +//printf("1. here\n"); } else if ( (Theta < (ThetaE + ThetaS)) && ( Theta > fabs(ThetaE - ThetaS))) { Type = LGM_PENUMBRAL_ECLIPSE; +//printf("2. here\n"); } else { Type = LGM_NO_ECLIPSE; +//printf("3. here\n"); } diff --git a/libLanlGeoMag/Lgm_Ellipsoid.c b/libLanlGeoMag/Lgm_Ellipsoid.c index 1bcb5487c..3aa2c35e6 100644 --- a/libLanlGeoMag/Lgm_Ellipsoid.c +++ b/libLanlGeoMag/Lgm_Ellipsoid.c @@ -42,10 +42,14 @@ int Lgm_EllipsoidIntersect( EllipsoidType *Ellipsoid, RayType *Ray, double *tmin D = sqrt(D); // compute Q - Q = 0.5*( (B<0.0) ? -B - D : -B + D ); - - t0 = Q/A; - t1 = C/Q; + //Q = 0.5*( (B<0.0) ? -B - D : -B + D ); + // + //t0 = Q/A; + //t1 = C/Q; + + Q = 0.5/A; + t0 = (-B - D)*Q; + t1 = (-B + D)*Q; if ( t0 < t1 ){ diff --git a/libLanlGeoMag/Lgm_FirstInvariant.c b/libLanlGeoMag/Lgm_FirstInvariant.c new file mode 100644 index 000000000..c9a9d8d36 --- /dev/null +++ b/libLanlGeoMag/Lgm_FirstInvariant.c @@ -0,0 +1,1293 @@ +#include "Lgm/Lgm_MagModelInfo.h" + +/** + * \brief + * Set up a coordinate system to represent full orbit particle velosities + * with pitch angle an gyrophase angles. + * + * \details + * Get the a, c, and b unit vectors, given q, phi and mInfo. q is the + * position of the particle, phi is the gyrophase (defined in terms of + * the two coordinate systems constructed here.) + * + * Set up the coordinate system that described the particle position + * + * We need to define a coordinate system that is perpendicular to the + * B-field. See Figure 1 of Weyssow and Balescu, (1986), Hamiltonian + * theory of guiding center motion revisited, J. Plasma Physics, 35, + * 449-471. However, we want the phi angle to go the other way around. + * I.e. when looking down from +b-hat side, we want phi to increase CCW from the +e1 axi + + * + * q: position of the particle + * b: magnetic field direction at q + * + * r: outward radial unit vector + * e2: is an eastward-point unit vector that is perpenmdilcular to both b and r. + * e1: completes RHS + * + * + * \raisewarning{In general we would need to take care of the case + * where the radius is +/- b-hat: Maybe by defing the r vector as the + * local radius of curvature vector or somthing.} + + * + * + * \param[in] q Position (in GSM) to use. + * \param[in] Mass Mass of particle. + * \param[in] RestEnergy RestEnergy of particle. + * \param[in] Energy Kinetic Energy of particle. + * \param[in] Delta Particle pitch angle. + * \param[in] Phi Particle gyro-phase angle. + * \param[out] a The a-hat vector of the moving particle frame + * \param[out] c The c-hat vector of the moving particle frame. + * \param[out] b The b-hat vector of the moving particle frame. This is direction along magnetic field vector. + * \param[out] v The velocity vector in GSM. km/s + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + * + */ +void Lgm_Set_Particle_Frames( Lgm_Vector *q, double Mass, double RestEnergy, double Energy, double Delta, double Phi, Lgm_Vector *a, Lgm_Vector *c, Lgm_Vector *b, Lgm_Vector *v, Lgm_MagModelInfo *mInfo ) { + + Lgm_Vector r, e1, e2; + //double m; + double SinPhi, CosPhi, vel, w, u, gamma; + //double m; + + r.x = q->x; r.y = q->y; r.z = q->z; + Lgm_NormalizeVector( &r ); //r-hat + + mInfo->Bfield( q, b, mInfo ); + Lgm_NormalizeVector( b ); //b-hat + + //Lgm_CrossProduct( &r, b, &e1 ); + Lgm_CrossProduct( b, &r, &e2 ); // Weyssow and Belescu, Burby definition + Lgm_NormalizeVector( &e2 ); // e2-hat + + Lgm_CrossProduct( &e2, b, &e1 ); // Weyssow and Belescu, Burby definition + Lgm_NormalizeVector( &e1 ); // e2-hat + + + /* + * Now construct a moving local frame. It is rotated by angle phi + * around b-hat. I.e.; + * + * c-hat = -sin(phi) e1-hat - cos(phi) e2_hat + * a-hat = cos(phi) e1-hat - sin(phi) e2_hat + + * a-hat = -sin(phi) e1-hat - cos(phi) e2_hat // Weyssow and Belescu, Burby definition + * c-hat = cos(phi) e1-hat - sin(phi) e2_hat // Weyssow and Belescu, Burby definition + * + */ + CosPhi = cos(Phi*RadPerDeg); SinPhi = sin(Phi*RadPerDeg); // these are computed over and over again needlessly. + + // a-hat + a->x = -SinPhi*e1.x - CosPhi*e2.x; + a->y = -SinPhi*e1.y - CosPhi*e2.y; + a->z = -SinPhi*e1.z - CosPhi*e2.z; + + // c-hat + c->x = CosPhi*e1.x - SinPhi*e2.x; + c->y = CosPhi*e1.y - SinPhi*e2.y; + c->z = CosPhi*e1.z - SinPhi*e2.z; + + //m = Mass; // kg + gamma = 1.0 + Energy/RestEnergy; + vel = sqrt( 1.0 - 1.0/(gamma*gamma) ) * LGM_c; // m/s + w = vel*sin(Delta*RadPerDeg); // v_perp, m/s + u = vel*cos(Delta*RadPerDeg); // v_par, m/s + + // Construct velocity vector +// v->x = u*b->x + w*c->x; // m/s +// v->y = u*b->y + w*c->y; // m/s +// v->z = u*b->z + w*c->z; // m/s + + v->x = u*b->x + w*a->x; // m/s + v->y = u*b->y + w*a->y; // m/s + v->z = u*b->z + w*a->z; // m/s + + + return; + +} + + +/** + * \brief + * Compute the gradient of Curl_b at a given point. Note - this is a tensor (and b is b-hat). + * + * \details + * Computes \f$ \nabla(\nabla\times \hat{b})\f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Grad_Cb The computed gradient of CurlB at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_Cb( Lgm_Vector *u0, double Grad_Cb[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, fx[7][3], fy[7][3], fz[7][3]; + int i, N; + Lgm_Vector u, Curlb; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_Curlb( &u, &Curlb, LGM_DERIV_SIX_POINT, 1e-4, m ); + fx[i+N][0] = Curlb.x; //Re^-1 + fx[i+N][1] = Curlb.y; //Re^-1 + fx[i+N][2] = Curlb.z; //Re^-1 + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_Curlb( &u, &Curlb, LGM_DERIV_SIX_POINT, 1e-4, m ); + fy[i+N][0] = Curlb.x; // Re^-1 + fy[i+N][1] = Curlb.y; // Re^-1 + fy[i+N][2] = Curlb.z; // Re^-1 + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_Curlb( &u, &Curlb, LGM_DERIV_SIX_POINT, 1e-4, m ); + fz[i+N][0] = Curlb.x; // Re^-1 + fz[i+N][1] = Curlb.y; // Re^-1 + fz[i+N][2] = Curlb.z; // Re^-1 + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + for (i=0; i<3; i++ ){ +//THESE ARE IN REVERSE ORDER I THNK? CHECK + Grad_Cb[0][i] = (fx[6][i] - 9.0*fx[5][i] + 45.0*fx[4][i] - 45.0*fx[2][i] + 9.0*fx[1][i] - fx[0][i])/(60.0*h); // Re^-2 + Grad_Cb[1][i] = (fy[6][i] - 9.0*fy[5][i] + 45.0*fy[4][i] - 45.0*fy[2][i] + 9.0*fy[1][i] - fy[0][i])/(60.0*h); // Re^-2 + Grad_Cb[2][i] = (fz[6][i] - 9.0*fz[5][i] + 45.0*fz[4][i] - 45.0*fz[2][i] + 9.0*fz[1][i] - fz[0][i])/(60.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + for (i=0; i<3; i++ ){ + Grad_Cb[0][i] = (-fx[4][i] + 8.0*fx[3][i] - 8.0*fx[1][i] + fx[0][i])/(12.0*h); // Re^-2 + Grad_Cb[1][i] = (-fy[4][i] + 8.0*fy[3][i] - 8.0*fy[1][i] + fy[0][i])/(12.0*h); // Re^-2 + Grad_Cb[2][i] = (-fz[4][i] + 8.0*fz[3][i] - 8.0*fz[1][i] + fz[0][i])/(12.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + for (i=0; i<3; i++ ){ + Grad_Cb[0][i] = (fx[2][i] - fx[0][i])/(2.0*h); // Re^-2 + Grad_Cb[1][i] = (fy[2][i] - fy[0][i])/(2.0*h); // Re^-2 + Grad_Cb[2][i] = (fz[2][i] - fz[0][i])/(2.0*h); // Re^-2 + } + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + return; + +} + + +/** + * \brief + * Compute the gradient of Gradient{ b-hat } at a given point. Note - this is a tensor + * + * \details + * Computes \f$ \nabla(\hat{b})\f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Grad_b The computed gradient of b at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_b( Lgm_Vector *u0, double Grad_b[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, fx[7][3], fy[7][3], fz[7][3]; + int i, N; + Lgm_Vector u, b; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fx[i+N][0] = b.x; + fx[i+N][1] = b.y; + fx[i+N][2] = b.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fy[i+N][0] = b.x; + fy[i+N][1] = b.y; + fy[i+N][2] = b.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fz[i+N][0] = b.x; + fz[i+N][1] = b.y; + fz[i+N][2] = b.z; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + for (i=0; i<3; i++ ){ + Grad_b[0][i] = (fx[6][i] - 9.0*fx[5][i] + 45.0*fx[4][i] - 45.0*fx[2][i] + 9.0*fx[1][i] - fx[0][i])/(60.0*h); // Re^-1 + Grad_b[1][i] = (fy[6][i] - 9.0*fy[5][i] + 45.0*fy[4][i] - 45.0*fy[2][i] + 9.0*fy[1][i] - fy[0][i])/(60.0*h); // Re^-1 + Grad_b[2][i] = (fz[6][i] - 9.0*fz[5][i] + 45.0*fz[4][i] - 45.0*fz[2][i] + 9.0*fz[1][i] - fz[0][i])/(60.0*h); // Re^-1 + } + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + for (i=0; i<3; i++ ){ + Grad_b[0][i] = (-fx[4][i] + 8.0*fx[3][i] - 8.0*fx[1][i] + fx[0][i])/(12.0*h); // Re^-1 + Grad_b[1][i] = (-fy[4][i] + 8.0*fy[3][i] - 8.0*fy[1][i] + fy[0][i])/(12.0*h); // Re^-1 + Grad_b[2][i] = (-fz[4][i] + 8.0*fz[3][i] - 8.0*fz[1][i] + fz[0][i])/(12.0*h); // Re^-1 + } + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + for (i=0; i<3; i++ ){ + Grad_b[0][i] = (fx[2][i] - fx[0][i])/(2.0*h); // Re^-1 + Grad_b[1][i] = (fy[2][i] - fy[0][i])/(2.0*h); // Re^-1 + Grad_b[2][i] = (fz[2][i] - fz[0][i])/(2.0*h); // Re^-1 + } + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + return; + +} + +/** + * \brief + * Compute the gradient of Gradient{ GradB } at a given point. Note - this is a tensor + * + * \details + * Computes \f$ \nabla( \nabla B )\f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Grad_GradB The computed gradient of GradB at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_GradB( Lgm_Vector *u0, double Grad_GradB[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, fx[7][3], fy[7][3], fz[7][3]; + int i, N; + Lgm_Vector u, GradB; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-4, m ); + fx[i+N][0] = GradB.x; + fx[i+N][1] = GradB.y; + fx[i+N][2] = GradB.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-4, m ); + fy[i+N][0] = GradB.x; + fy[i+N][1] = GradB.y; + fy[i+N][2] = GradB.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_GradB( &u, &GradB, LGM_DERIV_SIX_POINT, 1e-4, m ); + fz[i+N][0] = GradB.x; + fz[i+N][1] = GradB.y; + fz[i+N][2] = GradB.z; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + for (i=0; i<3; i++ ){ + Grad_GradB[0][i] = (fx[6][i] - 9.0*fx[5][i] + 45.0*fx[4][i] - 45.0*fx[2][i] + 9.0*fx[1][i] - fx[0][i])/(60.0*h); // Re^-2 + Grad_GradB[1][i] = (fy[6][i] - 9.0*fy[5][i] + 45.0*fy[4][i] - 45.0*fy[2][i] + 9.0*fy[1][i] - fy[0][i])/(60.0*h); // Re^-2 + Grad_GradB[2][i] = (fz[6][i] - 9.0*fz[5][i] + 45.0*fz[4][i] - 45.0*fz[2][i] + 9.0*fz[1][i] - fz[0][i])/(60.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + for (i=0; i<3; i++ ){ + Grad_GradB[0][i] = (-fx[4][i] + 8.0*fx[3][i] - 8.0*fx[1][i] + fx[0][i])/(12.0*h); // Re^-2 + Grad_GradB[1][i] = (-fy[4][i] + 8.0*fy[3][i] - 8.0*fy[1][i] + fy[0][i])/(12.0*h); // Re^-2 + Grad_GradB[2][i] = (-fz[4][i] + 8.0*fz[3][i] - 8.0*fz[1][i] + fz[0][i])/(12.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + for (i=0; i<3; i++ ){ + Grad_GradB[0][i] = (fx[2][i] - fx[0][i])/(2.0*h); // Re^-2 + Grad_GradB[1][i] = (fy[2][i] - fy[0][i])/(2.0*h); // Re^-2 + Grad_GradB[2][i] = (fz[2][i] - fz[0][i])/(2.0*h); // Re^-2 + } + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + return; + +} + + + + +/** + * \brief + * Compute the d{b dot v}/dcomp at a given point. + * + * \details + * Computes \f$ \partial (\hat{b}\cdot\vec{v})/\partial comp \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[in] v Velocity vector (in GSM) to use. + * \param[in] comp seclects component (0=x, 1=y, 2=z) + * \param[out] b dot v The computed partial deriviative { d dot v}/dcomp at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_dbdotvdcomp( Lgm_Vector *u0, Lgm_Vector *v, int comp, double *dbdotvdcomp, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double bdotv, H, fc[7]; + int i, N=3; + Lgm_Vector u, b; + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_dbdotvdcomp: Computing dbdotvdcomp for comp=%d with DerivScheme = %d, h = %g", comp, DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; + if ( comp == 0 ) { u.x += H; } + else if ( comp == 1 ) { u.y += H; } + else if ( comp == 2 ) { u.z += H; } + else { printf("\t\tLgm_dbdotvdcomp: Invalid component. comp = %d (must be 0, 1, or 2).\n", comp); exit(0);} + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); //b-hat + bdotv = Lgm_DotProduct( &b, v ); + + fc[i+N] = bdotv; + } + } + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + *dbdotvdcomp = (fc[6] - 9.0*fc[5] + 45.0*fc[4] - 45.0*fc[2] + 9.0*fc[1] - fc[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + *dbdotvdcomp = (-fc[4] + 8.0*fc[3] - 8.0*fc[1] + fc[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + *dbdotvdcomp = (fc[2] - fc[0])/(2.0*h); + } + if (m->VerbosityLevel > 0) printf(" dbdotvdcomp = %g (component = %d)\n", *dbdotvdcomp, comp); + + return; + +} +void Lgm_dbdotvdx( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdx, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dbdotvdcomp( u0, v, 0, dbdotvdx, DerivScheme, h, m ); } +void Lgm_dbdotvdy( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdy, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dbdotvdcomp( u0, v, 1, dbdotvdy, DerivScheme, h, m ); } +void Lgm_dbdotvdz( Lgm_Vector *u0, Lgm_Vector *v, double *dbdotvdz, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dbdotvdcomp( u0, v, 2, dbdotvdz, DerivScheme, h, m ); } +/** + * \brief + * Compute the Laplacian of b dot v at a given point. + * + * \details + * Computes \f$ \nabla^2 (\hat{b}\cdot\vec{v}) \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[in] v Velocity vector (in GSM) to use. + * \param[out] Laplacian_bdotv The computed Laplacian of bdotv at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Laplacian_bdotv( Lgm_Vector *u0, Lgm_Vector *v, double *Laplacian_bdotv, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, d2bdotvdx2, d2bdotvdy2, d2bdotvdz2; + double dbdotvdx, dbdotvdy, dbdotvdz; + double fx[7], gy[7], hz[7]; + int i, N=3; + Lgm_Vector u; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + + dbdotvdx = dbdotvdy = dbdotvdz = 0.0; + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_dbdotvdx( &u, v, &dbdotvdx, DerivScheme, h, m ); + fx[i+N] = dbdotvdx; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_dbdotvdy( &u, v, &dbdotvdy, DerivScheme, h, m ); + gy[i+N] = dbdotvdy; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_dbdotvdz( &u, v, &dbdotvdz, DerivScheme, h, m ); + hz[i+N] = dbdotvdz; + } + } + + + d2bdotvdx2 = d2bdotvdy2 = d2bdotvdz2 = 0.0; + if (DerivScheme == LGM_DERIV_SIX_POINT){ + d2bdotvdx2 = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])/(60.0*h); + d2bdotvdy2 = (gy[6] - 9.0*gy[5] + 45.0*gy[4] - 45.0*gy[2] + 9.0*gy[1] - gy[0])/(60.0*h); + d2bdotvdz2 = (hz[6] - 9.0*hz[5] + 45.0*hz[4] - 45.0*hz[2] + 9.0*hz[1] - hz[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + d2bdotvdx2 = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])/(12.0*h); + d2bdotvdy2 = (-gy[4] + 8.0*gy[3] - 8.0*gy[1] + gy[0])/(12.0*h); + d2bdotvdz2 = (-hz[4] + 8.0*hz[3] - 8.0*hz[1] + hz[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + d2bdotvdx2 = (hz[2] - hz[0])/(2.0*h); + d2bdotvdy2 = (hz[2] - hz[0])/(2.0*h); + d2bdotvdz2 = (hz[2] - hz[0])/(2.0*h); + } + *Laplacian_bdotv = d2bdotvdx2 + d2bdotvdy2 + d2bdotvdz2; + if (m->VerbosityLevel > 0) printf(" Laplacian_bdotv = %g\n", *Laplacian_bdotv ); + + return; + +} + + +/** + * \brief + * Compute the gradient of (b-hat dot v) at a given point. Note: This + * routine is designed to implement the Burby 1st invariant calculation + * and in those formulea, the v is to be taken as a constant with respect + * to derivatives. This could also be implemented using existing routines + * via some vector/tensor identities, but here I am just sticking literally + * to the Burby terms. + * + * \details + * Computes \f$ \nabla( \hat{B}\cdot\vec{v} ) \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[in] v Velocity vector. + * \param[out] Grad_bdotv The computed gradient of bdotv at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_bdotv( Lgm_Vector *u0, Lgm_Vector *v, Lgm_Vector *Grad_bdotv, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, fx[7], fy[7], fz[7]; + int i, N=3; + Lgm_Vector u, b; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fx[i+N] = Lgm_DotProduct( &b, v ); + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fy[i+N] = Lgm_DotProduct( &b, v ); + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fz[i+N] = Lgm_DotProduct( &b, v ); + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + Grad_bdotv->x = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])/(60.0*h); + Grad_bdotv->y = (fy[6] - 9.0*fy[5] + 45.0*fy[4] - 45.0*fy[2] + 9.0*fy[1] - fy[0])/(60.0*h); + Grad_bdotv->z = (fz[6] - 9.0*fz[5] + 45.0*fz[4] - 45.0*fz[2] + 9.0*fz[1] - fz[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + Grad_bdotv->x = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])/(12.0*h); + Grad_bdotv->y = (-fy[4] + 8.0*fy[3] - 8.0*fy[1] + fy[0])/(12.0*h); + Grad_bdotv->z = (-fz[4] + 8.0*fz[3] - 8.0*fz[1] + fz[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + Grad_bdotv->x = (fx[2] - fx[0])/(2.0*h); + Grad_bdotv->y = (fy[2] - fy[0])/(2.0*h); + Grad_bdotv->z = (fz[2] - fz[0])/(2.0*h); + } + if (m->VerbosityLevel > 0) printf(" Grad_bdotv = (%g %g %g)\n", Grad_bdotv->x, Grad_bdotv->y, Grad_bdotv->z ); + + return; + +} + +/** + * \brief + * Compute the gradient of Gradient{ GradB } at a given point. Note - this is a tensor + * + * \details + * Computes \f$ \nabla( \nabla B )\f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Grad_Grad_bdotv The computed gradient of GradB at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_Grad_bdotv( Lgm_Vector *u0, Lgm_Vector *v, double Grad_Grad_bdotv[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, fx[7][3], fy[7][3], fz[7][3]; + int i, N; + Lgm_Vector u, Grad_bdotv; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_Grad_bdotv( &u, v, &Grad_bdotv, LGM_DERIV_SIX_POINT, 1e-4, m ); + fx[i+N][0] = Grad_bdotv.x; + fx[i+N][1] = Grad_bdotv.y; + fx[i+N][2] = Grad_bdotv.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_Grad_bdotv( &u, v, &Grad_bdotv, LGM_DERIV_SIX_POINT, 1e-4, m ); + fy[i+N][0] = Grad_bdotv.x; + fy[i+N][1] = Grad_bdotv.y; + fy[i+N][2] = Grad_bdotv.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_Grad_bdotv( &u, v, &Grad_bdotv, LGM_DERIV_SIX_POINT, 1e-4, m ); + fz[i+N][0] = Grad_bdotv.x; + fz[i+N][1] = Grad_bdotv.y; + fz[i+N][2] = Grad_bdotv.z; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + for (i=0; i<3; i++ ){ + Grad_Grad_bdotv[0][i] = (fx[6][i] - 9.0*fx[5][i] + 45.0*fx[4][i] - 45.0*fx[2][i] + 9.0*fx[1][i] - fx[0][i])/(60.0*h); // Re^-2 + Grad_Grad_bdotv[1][i] = (fy[6][i] - 9.0*fy[5][i] + 45.0*fy[4][i] - 45.0*fy[2][i] + 9.0*fy[1][i] - fy[0][i])/(60.0*h); // Re^-2 + Grad_Grad_bdotv[2][i] = (fz[6][i] - 9.0*fz[5][i] + 45.0*fz[4][i] - 45.0*fz[2][i] + 9.0*fz[1][i] - fz[0][i])/(60.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + for (i=0; i<3; i++ ){ + Grad_Grad_bdotv[0][i] = (-fx[4][i] + 8.0*fx[3][i] - 8.0*fx[1][i] + fx[0][i])/(12.0*h); // Re^-2 + Grad_Grad_bdotv[1][i] = (-fy[4][i] + 8.0*fy[3][i] - 8.0*fy[1][i] + fy[0][i])/(12.0*h); // Re^-2 + Grad_Grad_bdotv[2][i] = (-fz[4][i] + 8.0*fz[3][i] - 8.0*fz[1][i] + fz[0][i])/(12.0*h); // Re^-2 + } + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + for (i=0; i<3; i++ ){ + Grad_Grad_bdotv[0][i] = (fx[2][i] - fx[0][i])/(2.0*h); // Re^-2 + Grad_Grad_bdotv[1][i] = (fy[2][i] - fy[0][i])/(2.0*h); // Re^-2 + Grad_Grad_bdotv[2][i] = (fz[2][i] - fz[0][i])/(2.0*h); // Re^-2 + } + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + return; + +} + + + +void Lgm_Dyad( Lgm_Vector *a, Lgm_Vector *b, double r[3][3] ) { + + r[0][0] = a->x*b->x; r[0][1] = a->x*b->y; r[0][2] = a->x*b->z; + r[1][0] = a->y*b->x; r[1][1] = a->y*b->y; r[1][2] = a->y*b->z; + r[2][0] = a->z*b->x; r[2][1] = a->z*b->y; r[2][2] = a->z*b->z; + + return; +} + +void Lgm_VectorDotTensor( Lgm_Vector *v, double T[3][3], Lgm_Vector *Result ) { + Result->x = v->x*T[0][0] + v->y*T[1][0] + v->z*T[2][0]; + Result->y = v->x*T[0][1] + v->y*T[1][1] + v->z*T[2][1]; + Result->z = v->x*T[0][2] + v->y*T[1][2] + v->z*T[2][2]; +} + +void Lgm_TensorDotVector( double T[3][3], Lgm_Vector *v, Lgm_Vector *Result ) { + Result->x = T[0][0]*v->x + T[0][1]*v->y + T[0][2]*v->z; + Result->y = T[1][0]*v->x + T[1][1]*v->y + T[1][2]*v->z; + Result->z = T[2][0]*v->x + T[2][1]*v->y + T[2][2]*v->z; +} +void Lgm_TensorDotTensor( double A[3][3], double B[3][3], double R[3][3] ) { + int i, j, k; + for ( i=0; i<3; i++ ) { + for ( j=0; j<3; j++ ) { + R[i][j] = 0.0; + for (k=0; k<3; k++) R[i][j] += A[i][k]*B[k][j]; + + } + } +} +double Lgm_TraceTensor( double T[3][3] ) { + return( T[0][0] + T[1][1] + T[2][2] ); +} +void Lgm_TensorTranspose( double T[3][3], double T_transpose[3][3] ) { + int i, j; + for ( i=0; i<3; i++ ) { + for ( j=0; j<3; j++ ) { + T_transpose[i][j] = T[j][i]; + } + } +} +double Lgm_DoubleDot( double A[3][3], double B[3][3] ) { + int i, j; + double sum; + for (sum=0.0, i=0; i<3; i++ ) { + for ( j=0; j<3; j++ ) { + sum += A[i][j]*B[i][j]; + } + } + return( sum ); +} + +double Lgm_dBdr( Lgm_Vector *q, Lgm_MagModelInfo *mInfo ){ + + int N, i; + double h, H, fr[7], dBdr; + double r, r0, B, Phi, Theta, SinTheta, CosTheta, SinPhi, CosPhi; + Lgm_Vector u, Bvec; + + N = 3; + h = 1e-4; + + r0 = Lgm_Magnitude( q ); // Re + SinTheta = q->z/r0; + Theta = asin( SinTheta ); + CosTheta = cos( Theta ); + Phi = atan2( q->y, q->x ); + SinPhi = sin( Phi ); + CosPhi = cos( Phi ); + + /* + * dB/dr + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + r = r0; H = (double)i*h; r += H; + u.x = r*CosPhi*CosTheta; + u.y = r*SinPhi*CosTheta; + u.z = r*SinTheta; + + mInfo->Bfield( &u, &Bvec, mInfo ); + B = Lgm_Magnitude( &Bvec ); // Teslas + + fr[i+N] = B; // Teslas + } + } + dBdr = (fr[6] - 9.0*fr[5] + 45.0*fr[4] - 45.0*fr[2] + 9.0*fr[1] - fr[0])/(60.0*h); // T/Re + + return( dBdr ); +} + + +double Lgm_d2Bdr2( Lgm_Vector *q, Lgm_MagModelInfo *mInfo ){ + + int N, i; + double h, H, fr[7], d2Bdr2; + double r, r0, Phi, Theta, SinTheta, CosTheta, SinPhi, CosPhi; + Lgm_Vector u; + + N = 3; + h = 1e-4; + + r0 = Lgm_Magnitude( q ); + SinTheta = q->z/r0; + Theta = asin( SinTheta ); + CosTheta = cos( Theta ); + Phi = atan2( q->y, q->x ); + SinPhi = sin( Phi ); + CosPhi = cos( Phi ); + + /* + * dB/dr + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + r = r0; H = (double)i*h; r += H; + u.x = r*CosPhi*CosTheta; + u.y = r*SinPhi*CosTheta; + u.z = r*SinTheta; + + fr[i+N] = Lgm_dBdr( &u, mInfo ); // dB/dr in T/m + } + } + d2Bdr2 = (fr[6] - 9.0*fr[5] + 45.0*fr[4] - 45.0*fr[2] + 9.0*fr[1] - fr[0])/(60.0*h); // T/m^2 + + return( d2Bdr2 ); +} + + + +/** + * \brief + * Compute the first adiabatic invariant up to second order terms + * + * \details + * Computes \f$ {\mu = \mu_0 + \epsilon\mu_1 + \epsilon^2\mu_2} \f$ using + * the formulae of Burby et al. (2013). Accurate to \f$ + * {\mathcal{O}(\epsilon^3)} \f$: + * + * J. W. Burby, J. Squire, H. Qin; Automation of the guiding center + * expansion. Physics of Plasmas 1 July 2013; 20 (7): 072105. + * https://doi.org/10.1063/1.4813247 + * + * See equations 29 and F1 for the first and second order terms. Also, + * note that this is the correct formula for general fields. (We have not + * yet provided a convenient means to hook up the electric field though.) + * + * Also note that to my knowledge this is the only general formula ever + * published that does not have errors in it. The Weyssow and Balescu + * formulas (see Weyssow, B. & Balescu, R. 1986 Hamiltonian theory of + * guiding centre motion revisited. J. Plasma Phys. 35, 449.) have typos + * in them. (As pointed out by Burby et al., in W&B, equation (81), the + * 3rd and 4th lines of a_13 have two terms that probably are a typo as + * they could have been combined trivially as written. In addition, I have + * found an error in 5th line of a_04: the 4B^-2 should be 5B^-2. There + * appear to be other errors also however, and I was never able to get W&B + * to work.) + * + * The Burby et al results were tested extensively against the Gardnder + * special case formula and the results match. + * + * \param[in] q Position (in GSM) to use. Re + * \param[in] v Velocity vector. m/s + * \param[in] Gamma The relativistic factor unitless + * \param[in] Mass The mass of the particle, kg + * \param[in] Charge The charge of the particle, C + * \param[out] mu0_out mu0 MeV/G + * \param[out] mu1_out eps*mu_1 (where eps = Gamma*m/e) MeV/G + * \param[out] mu2_out eps^2*mu_2 (where eps = Gamma*m/e) MeV/G + * + * + * \author Mike Henderson \date 2023 + * + */ +double Lgm_Mu_Burby( Lgm_Vector *q, Lgm_Vector *v, double Gamma, double Mass, double Charge, double *mu0_out, double *mu1_out, double *mu2_out, Lgm_MagModelInfo *mInfo ){ + + + Lgm_Vector b; // b-hat(q) + Lgm_Vector GradB; // Grad{ |B-vec| }(q) + double B; // |B-vec|((q) + Lgm_Vector L, N, K, H; // Burby's "lembda, eta, Kappa" vectors + double Grad_b[3][3]; // tensor + Lgm_Vector v_cross_b, b_cross_K, GradB_cross_b; + double G, M, f1, f2, f3, f4, m, e, eps, eps2, mu, mu0, mu1, mu2; + int i, j; + double B2, B3, v2, u, u2, u3, u4, w2, g, g2; + + e = Charge; //Coulombs (or s·A) + //c = LGM_c; // Speed of light m/s + m = Mass; // Mass of particle kg + + mInfo->Bfield( q, &b, mInfo ); // Get B-vec, nT + B = Lgm_NormalizeVector(&b); // Normalize vector and return the magntiude as B (in nT) + B *= 1e-9; // T + B2 = B*B; + B3 = B2*B; + + Lgm_GradB( q, &GradB, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); // Compute gradient of B-field at position q + g = 1e-9/(Re*1e3); + GradB.x *= g; // T/m + GradB.y *= g; // T/m + GradB.z *= g; // T/m + + // Compute Grad{ ln|B| } = Grad{B}/B + H = GradB; Lgm_ScaleVector( &H, 1.0/B ); + + // Compute G (Burby's "gamma" scalar. Note that Grad{ln|B|} = Grad{|B|}/B ) + G = Lgm_DotProduct( v, &GradB )/B; // 1/s + + // Compute M (Burby's "mu" scalar) + Lgm_CrossProduct( v, &b, &v_cross_b ); + M = 0.5*Lgm_DotProduct( &v_cross_b, &v_cross_b )/B; // m^2/s^2 / T + + // Compute u (Burby's "v_par" scalar) + u = Lgm_DotProduct( &b, v ); + u2 = u*u; // m^2/s^2 + u3 = u2*u; + u4 = u2*u2; + v2 = Lgm_DotProduct( v, v ); + w2 = v2 - u2; + eps = Gamma*m/e; // Gamma is given to us. + eps2 = eps*eps; + + // Compute L (Burby's "lambda" vector) + g = 1.0/(Re*1e3); + Lgm_Grad_b( q, Grad_b, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + for (i=0;i<3;i++){ for (j=0;j<3;j++){ Grad_b[i][j] *= g; } } // 1/m + Lgm_VectorDotTensor( v, Grad_b, &L ); // This is a vector dotted with a tensor = vector + + // Compute N (Burby's "eta" vector) + Lgm_TensorDotVector( Grad_b, v, &N ); // This is a tensor dotted vector = vector + + // Compute K (Burby's "kappa" vector = b dot Grad(b-hat) + Lgm_VectorDotTensor( &b, Grad_b, &K ); // This is a vector dotted with a tensor = vector + + + + /* + * mu0 + */ + mu0 = Gamma*m*w2/(2.0*B); // J/T + + + // Compute terms in brackets in Burby's eqn 29 + f1 = 0.25*u * Lgm_DotProduct( &L, &v_cross_b ); + f2 = -0.75*u * Lgm_DotProduct( &v_cross_b, &N ); + Lgm_CrossProduct( &b, &K, &b_cross_K ); + f3 = -1.25*u2 * Lgm_DotProduct( &b_cross_K, v ); + Lgm_CrossProduct( &GradB, &b, &GradB_cross_b ); + f4 = M * Lgm_DotProduct( &GradB_cross_b, v ); + //mu1 = 0.5*Gamma*m*(f1 + f2 + f3 + f4)/(B*B); + mu1 = Gamma*m*(f1 + f2 + f3 + f4)/(B*B); // seems to be off by exactly 2 + + + //printf("BURBY: mu1 = %g\n", mu1); + //printf("BURBY: mu1 = %.10g MeV/G\n", eps*mu1 * 6.242e12 / 1.0e4); + + + + + /* + * mu2 + */ + mu2 = -71.0*M* Lgm_DotProduct(&N, &N)/(128.0*B2); + mu2 += -5.0*u*M * Lgm_DotProduct(&L, &H)/(3.0*B2); + mu2 += 10.0*u*M*Lgm_DotProduct(&H, &N)/(3.0*B2); + mu2 += -715.0*u*M*Lgm_DotProduct(&K, &N)/(192.0*B2); + double Kdotv; + Kdotv = Lgm_DotProduct(&K, v); + mu2 += 71.0*M*Kdotv*Kdotv/(128.0*B2); + mu2 += -3.0*M*G*G/(2.0*B2); + mu2 += 71.0*u*M*Lgm_DotProduct(&L, &K)/(64.0*B2); + double LdotL; + LdotL = Lgm_DotProduct( &L, &L ); + mu2 += -7.0*M*LdotL/(128.0*B2); + double LdotN; + LdotN = Lgm_DotProduct( &L, &N ); + mu2 += 25.0*M*LdotN/(64.0*B2); + double Ldotv; + Ldotv = Lgm_DotProduct( &L, v ); + mu2 += 5.0*u*Kdotv*Ldotv/(12.0*B3); + mu2 += -2.0*u*G*Ldotv/(3.0*B3); + mu2 += Ldotv*Ldotv/(8.0*B3); + double Divb; + Lgm_Divb( q, &Divb, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); Divb /= (Re*1e3); + mu2 += 217.0*u*M*Kdotv*Divb/(32.0*B2); + mu2 += -5.0*u*M*G*Divb/(3.0*B2); + mu2 += 23.0*M*Ldotv*Divb/(32.0*B2); + + double Laplacian_bdotv; + Lgm_Laplacian_bdotv( q, v, &Laplacian_bdotv, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + g = 1.0/(Re*1e3); g2 = g*g; Laplacian_bdotv *= g2; + mu2 += -5.0*u*M*Laplacian_bdotv/(6.0*B2); + + Lgm_Vector Grad_Divb; + Lgm_Grad_Divb( q, &Grad_Divb, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + g = 1.0/(Re*1e3); g2 = g*g; Grad_Divb.x *= g2; Grad_Divb.y *= g2; Grad_Divb.z *= g2; + mu2 += 5.0*u*M*Lgm_DotProduct( v, &Grad_Divb )/(6.0*B2); + + double vv[3][3]; + double Grad_GradB[3][3]; + Lgm_Dyad( v, v, vv ); + Lgm_Grad_GradB( q, Grad_GradB, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + g = 1.0/(Re*1e3); g2 = 1e-9*g*g; + for (i=0;i<3;i++){ for (j=0;j<3;j++){ Grad_GradB[i][j] *= g2; } } // 1/m + mu2 += M/(2.0*B3) * Lgm_DoubleDot( vv, Grad_GradB ); + + double bb[3][3]; + double Grad_Grad_bdotv[3][3]; + Lgm_Dyad( &b, &b, bb ); + Lgm_Grad_Grad_bdotv( q, v, Grad_Grad_bdotv, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + g = 1.0/(Re*1e3); g2 = g*g; + for (i=0;i<3;i++){ for (j=0;j<3;j++){ Grad_Grad_bdotv[i][j] *= g2; } } // 1/m + mu2 += 5.0*u*M/(6.0*B2) * Lgm_DoubleDot( bb, Grad_Grad_bdotv ); + mu2 += u/(6.0*B3) * Lgm_DoubleDot( vv, Grad_Grad_bdotv ); + mu2 += -25.0*u2*M/(12.0*B2) * Lgm_DotProduct( &b, &Grad_Divb ); + + double NdotN; + NdotN = Lgm_DotProduct( &N, &N ); + mu2 += -5.0*u2*NdotN/(8.0*B3); + mu2 += 20.0*u2*M*Lgm_DotProduct( &K, &H )/(3.0*B2); + double KdotK = Lgm_DotProduct( &K, &K ); + mu2 += -3013.0*u2*M*KdotK/(384.0*B2); + mu2 += 5.0*u2*Kdotv*Kdotv/(6.0*B3); + mu2 += -5.0*u2*Kdotv*G/(6.0*B3); + mu2 += -29.0*u2*LdotL/(24.0*B3); + mu2 += 5.0*u2*LdotN/(2.0*B3); + mu2 += -5.0*u2*Ldotv*Divb/(12.0*B3); + mu2 += -25.0*u2*M*Divb*Divb/(24.0*B2); + + double Grad_b_T[3][3]; + double Gradb_dot_GradbT[3][3]; + double Trace_Gradb_dot_GradbT; + Lgm_TensorTranspose( Grad_b, Grad_b_T ); + Lgm_TensorDotTensor( Grad_b, Grad_b_T, Gradb_dot_GradbT ); + Trace_Gradb_dot_GradbT = Lgm_TraceTensor( Gradb_dot_GradbT ); + //printf("Trace_Gradb_dot_GradbT = %g\n", Trace_Gradb_dot_GradbT); + mu2 += 55.0*u2*M/(24.0*B2)*Trace_Gradb_dot_GradbT; + + double Gradb_dot_Gradb[3][3]; + double Trace_Gradb_dot_Gradb; + Lgm_TensorDotTensor( Grad_b, Grad_b, Gradb_dot_Gradb ); + Trace_Gradb_dot_Gradb = Lgm_TraceTensor( Gradb_dot_Gradb ); + //printf("Trace_Gradb_dot_Gradb = %g\n", Trace_Gradb_dot_Gradb); + mu2 += -35.0*u2*M/(8.0*B2)*Trace_Gradb_dot_Gradb; + + // get bv dyad + double bv[3][3]; + Lgm_Dyad( &b, v, bv ); + mu2 += 5.0*u2/(12.0*B3) * Lgm_DoubleDot( bv, Grad_Grad_bdotv ); + + double LdotK; + LdotK = Lgm_DotProduct( &L, &K ); + mu2 += 5.0*u3*LdotK/(3.0*B3); + mu2 += 5.0*u3*Kdotv*Divb/(12.0*B3); + mu2 += 5.0*u3/(12.0*B3) * Lgm_DoubleDot( bb, Grad_Grad_bdotv ); + + mu2 += 25.0*u4*KdotK/(24.0*B3); + mu2 += -5.0*M*M/(4.0*B)*Lgm_DotProduct( &b, &Grad_Divb ); + double LaplacianB; + Lgm_LaplacianB( q, &LaplacianB, LGM_DERIV_SIX_POINT, 1e-5, mInfo ); + g = 1.0/(Re*1e3); g2 = 1e-9*g*g; LaplacianB *= g2; + mu2 += -5.0*M*M*LaplacianB/(4.0*B2); + mu2 += 15.0*M*M* Lgm_DotProduct( &H, &H )/(4.0*B); + mu2 += -5.0*M*M*Lgm_DotProduct( &K, &H )/(4.0*B); + mu2 += -11.0*M*M*KdotK/(64.0*B); + mu2 += -133.0*M*M*Divb*Divb/(32.0*B); + mu2 += 11.0*M*M/(64.0*B)*Trace_Gradb_dot_GradbT; + mu2 += -5.0*M*M/(64.0*B)*Trace_Gradb_dot_Gradb; + + mu2 *= Gamma*m; + //printf("BURBY: mu2 = %g MeV/G\n", eps2*mu2 * 6.242e12 / 1.0e4); + //printf("BURBY: mu = %g MeV/G\n", (mu0 + eps*mu1 + eps2*mu2) * 6.242e12 / 1.0e4); + + + mu = (mu0 + eps*mu1 + eps2*mu2) * 6.242e12 / 1.0e4; + + *mu0_out = mu0*6.242e12/1.0e4; + *mu1_out = eps*mu1*6.242e12/1.0e4; + *mu2_out = eps2*mu2*6.242e12/1.0e4; + + return( mu ); + +} + + +/** + * \brief + * Compute the gradient of (b-hat dot v) at a given point. Note: This + * routine is designed to implement the Burby 1st invariant calculation + * and in those formulea, the v is to be taken as a constant with respect + * to derivatives. This could also be implemented using existing routines + * via some vector/tensor identities, but here I am just sticking literally + * to the Burby terms. + * + * \details + * Computes \f$ \nabla( \hat{B}\cdot\vec{v} ) \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[in] v Velocity vector. + * \param[out] Grad_bdotv The computed gradient of bdotv at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +double Lgm_Mu_Gardner( Lgm_Vector *q, Lgm_Vector *v, double Gamma, double Mass, double Charge, double *mu0_out, double *mu1_out, double *mu2_out, Lgm_MagModelInfo *mInfo ){ + + //double Omega; + double vel, v2, w, w2, w4, u, u2, eps, eps2, g; + double B, B2, B3, B4, B5, mu0, mu1, mu2; + double dBdr, d2Bdr2, r; + double vtheta, vtheta2, Delta, SinDelta, CosDelta, SinPhi, CosPhi; + Lgm_Vector Bvec, bhat, rhat, e1, e2, a, c; + + g = 1e-9/(Re*1e3); + dBdr = g*Lgm_dBdr( q, mInfo ); // dB/dr T/m + + g = 1e-9/(Re*Re*1e6); + d2Bdr2 = g*Lgm_d2Bdr2( q, mInfo ); // dB/dr T/m^2 + + r = Lgm_Magnitude( q )*Re*1e3; + rhat = *q; Lgm_NormalizeVector( &rhat ); + + + mInfo->Bfield( q, &Bvec, mInfo ); + B = 1e-9*Lgm_Magnitude( &Bvec ); // Teslas + B2 = B*B; B3 = B2*B; B4 = B3*B; B5 = B3*B2; + bhat = Bvec; Lgm_NormalizeVector( &bhat ); + + + /* + * Get vector in the theta direction + */ + Lgm_CrossProduct( &bhat, &rhat, &e2 ); // Weyssow and Belescu, Burby definition + Lgm_NormalizeVector( &e2 ); // e2-hat + + vel = Lgm_Magnitude( v ); v2 = vel*vel; + u = Lgm_DotProduct( v, &bhat ); // v_par + w = sqrt( v2 - u*u ); + w2 = w*w; + w4 = w2*w2; + u2 = u*u; + + eps = Gamma*Mass/Charge; + eps2 = eps*eps; + + + /* + * mu0 + */ + mu0 = Gamma*Mass*w2/(2.0*B); // J/T + //printf("\nGARDNER: mu0 = %g MeV/G\n", mu0 * 6.242e12 / 1.0e4 ); + + //vtheta = vel*SinDelta*SinPhi; + vtheta = Lgm_DotProduct( v, &e2); + vtheta2 = vtheta*vtheta; + + + /* + * mu1 + */ + mu1 = -Gamma*Mass*dBdr*( v2 + u2 ) * vtheta /B3; +mu1 *= 0.5; + //mu1 = -dBdr*( v*v + u*u ) * vtheta /(2.0*B3); + //printf("GARDNER: mu1 = %g\n", mu1 ); + //printf("GARDNER: mu1 = %.10g MeV/G\n", eps*mu1 * 6.242e12 / 1.0e4 ); + + + /* + * mu2 + */ + mu2 = -d2Bdr2/(2.0*B4)*( vtheta2*u2 + (vtheta2+0.25*w2)*(v2+u2) ); + mu2 += dBdr*dBdr/B5 * ( 0.5*(3.0*vtheta2+u2)*(v2+u2) + 3.0/8.0*w4); + mu2 += dBdr/(2.0*r*B4) * (vtheta2*v2 - w2*u2 - 1.25*w2*(v2+u2) + 2.0*vtheta2*u2); + mu2 *= 0.5*Gamma*Mass; +//mu2 *= 0.5; + //printf("GARDNER: mu2 = %.10g MeV/G\n", eps2*mu2 * 6.242e12 / 1.0e4 ); + //printf("GARDNER: mu = %g MeV/G\n", (mu0 + eps*mu1 + eps2*mu2 ) * 6.242e12 / 1.0e4); + + return( (mu0 + eps*mu1 + eps2*mu2 ) * 6.242e12 / 1.0e4 ); + +} + diff --git a/libLanlGeoMag/Lgm_FluxToPsd.c b/libLanlGeoMag/Lgm_FluxToPsd.c index df8574b0a..2c04f86b6 100644 --- a/libLanlGeoMag/Lgm_FluxToPsd.c +++ b/libLanlGeoMag/Lgm_FluxToPsd.c @@ -13,6 +13,8 @@ #include "Lgm/Lgm_CTrans.h" #include "Lgm/Lgm_MagModelInfo.h" #include "Lgm/Lgm_DynamicMemory.h" +#include "Lgm/GPR.h" +//int FLAGFLAG=0; void praxis( int n, double *x, int *data, double (*funct)(double *, void *data), double *in, double *out); @@ -29,6 +31,7 @@ typedef struct _FitData { int n; double *E; double *g; + double *dg; } _FitData; @@ -121,7 +124,7 @@ double Lgm_Mu_to_Ek( double Mu, double a, double B, double E0 ) { if ( sa2 < 1e-12 ) { return( 9e99 ); } else { - Ek = sqrt( 2*E0*B*Mu/(sa2*nT_Per_Gauss) + E0*E0) - E0; + Ek = sqrt( 2.0*E0*B*Mu/(sa2*nT_Per_Gauss) + E0*E0) - E0; return( Ek ); } @@ -281,6 +284,42 @@ double Lgm_DiffFluxToPsd( double j, double p2c2 ){ } +/** + * \brief + * Convert differential flux to (non relativistic velocity space) phase space density. + * \details + * The basic relationship is; + * \f[ + * f = {(m^2\over 2E)} j + * \f] + * + * \param[in] j Differential Flux in units of #/cm^2/s/sr/eV + * \param[in] m non-relativistic mass kg + * \param[in] E non-relativistic Energy eV + * + * \return f, Phase space density in units of s^3/cm^6 + * + * \author Mike Henderson + * \date 2021 + * + */ +double Lgm_DiffFluxToPsd2( double j, double m, double E ){ + + double eVPerJoule, g, K, f; + + // f = (m^2/(2E)) * j + // convert mass fro kg to eV^2 s^4/cm^4 + // 1J = 1kg m^2/s^2 = 6.242e18 eV + eVPerJoule = 6.241509e18; // eV/J + g = m * eVPerJoule; + K = 0.5*g*g; // ev^2 s^4/m^4 + K /= 1.0e8; // ev^2 s^4/cm^4 + + f = K * j/E; + + return( f ); // f in units of s^3/cm^6 +} + /** @@ -346,6 +385,7 @@ Lgm_FluxToPsd *Lgm_F2P_CreateFluxToPsd( int DumpDiagnostics ) { f->Extrapolate = TRUE; f->nMaxwellians = 2; f->FitType = LGM_F2P_SPLINE; + f->FitType = LGM_F2P_MAXWELLIAN; f->Alloced1 = FALSE; f->Alloced2 = FALSE; @@ -438,7 +478,12 @@ void Lgm_F2P_SetObservedB( double B_obs, Lgm_FluxToPsd *f ) { /** * \brief - * Adds (to a Lgm_FluxToPsd structure) the user-supplied arrays containing J[Energy][Alpha], Energy[], Alpha[] + * Adds (to a Lgm_FluxToPsd structure) the user-supplied arrays containing + * J[Energy][Alpha], Energy[], Alpha[]. Also computes the PSD (i.e. f as a + * function of energy and Alpha). Note that f=j/p^2, so this is not very + * much work. The hard work following these steps is to compute f at the + * given fixed mu's and K's and that requires interpolation/extrapolation, + * etc. * \details * * \param[in] J 2D array containing the differential flux values as a function of energy and pitch angle. @@ -452,11 +497,11 @@ void Lgm_F2P_SetObservedB( double B_obs, Lgm_FluxToPsd *f ) { * \date 2010-2011 * */ -void Lgm_F2P_SetFlux( double **J, double *E, int nE, double *A, int nA, Lgm_FluxToPsd *f ) { +void Lgm_F2P_SetFlux( double **J, double **dJ, double *E, int nE, double *A, int nA, Lgm_FluxToPsd *f ) { int i, j; - double flux, p2c2, fp, Min, Max; + double flux, dflux, p2c2, fp, dfp, Min, Max; /* @@ -465,70 +510,68 @@ void Lgm_F2P_SetFlux( double **J, double *E, int nE, double *A, int nA, Lgm_Flux if ( f->Alloced1 ) { LGM_ARRAY_1D_FREE( f->E ); LGM_ARRAY_1D_FREE( f->A ); + LGM_ARRAY_1D_FREE( f->Aeq ); LGM_ARRAY_2D_FREE( f->FLUX_EA ); + LGM_ARRAY_2D_FREE( f->dFLUX_EA ); LGM_ARRAY_2D_FREE( f->PSD_EA ); + LGM_ARRAY_2D_FREE( f->dPSD_EA ); } /* * Add Flux array to f structure. Alloc arrays appropriately. */ - f->nE = nE; - f->nA = nA; - LGM_ARRAY_1D( f->E, f->nE, double ); - LGM_ARRAY_1D( f->A, f->nA, double ); - LGM_ARRAY_2D( f->FLUX_EA, f->nE, f->nA, double ); + f->nE = nE; // Number of Energies + f->nA = nA; // Number of Pitch Angles + + LGM_ARRAY_1D( f->E, f->nE, double ); // Values for the Energies + LGM_ARRAY_1D( f->A, f->nA, double ); // Values for the Pitch Angles + LGM_ARRAY_1D( f->Aeq, f->nA, double ); // Values for the Eq. Pitch Angles + LGM_ARRAY_2D( f->FLUX_EA, f->nE, f->nA, double ); // Differential Flux as a function of Energy and Pitch Angle. + LGM_ARRAY_2D( f->dFLUX_EA, f->nE, f->nA, double ); // Unc. in Differential Flux as a function of Energy and Pitch Angle. + LGM_ARRAY_2D( f->PSD_EA, f->nE, f->nA, double ); // PSD as a function of Energy and Pitch Angle. + LGM_ARRAY_2D( f->dPSD_EA, f->nE, f->nA, double ); // Unc. in PSD as a function of Energy and Pitch Angle. + f->Alloced1 = TRUE; // Flag that we have alloced this mem. + for (i=0; inE; i++) f->E[i] = E[i]; for (i=0; inA; i++) f->A[i] = A[i]; - for (i=0; inE; i++) { - for (j=0; jnA; j++) { - f->FLUX_EA[i][j] = J[i][j]; // FLUX_EA is "Flux versus Energy and Pitch Angle". - } - } - /* - * Fill in gaps in the array. - */ - // for each energy, find bad points and fill them. -/* for (i=0; inE; i++) { - for (j=1; jnA-1; j++) { - // if there is a value on each side, average it. - if ( f->FLUX_EA[i][j] < 0.0 ) { - - } + for (j=0; jnA; j++) { + f->FLUX_EA[i][j] = J[i][j]; // FLUX_EA is "Differential Flux versus Energy and Pitch Angle". + f->dFLUX_EA[i][j] = dJ[i][j]; // uncertainty in FLUX_EA } } -*/ - - - if ( f->DumpDiagnostics ) { - DumpGif( "Lgm_FluxToPsd_FLUX_EA", f->nA, f->nE, f->FLUX_EA ); - } + if ( f->DumpDiagnostics ) { DumpGif( "Lgm_FluxToPsd_FLUX_EA", f->nA, f->nE, f->FLUX_EA ); } /* - * Alloc mem for the PSD array. * Convert Flux array into to PSD array. * Note, the result here is not PSD at constant Mu and K, it is PSD at the * same Es and Alphas we started with. */ - LGM_ARRAY_2D( f->PSD_EA, f->nE, f->nA, double ); for (j=0; jnA; j++) { for (i=0; inE; i++) { + flux = f->FLUX_EA[i][j]; + dflux = f->dFLUX_EA[i][j]; + p2c2 = (f->E[i] >= 0.0 ) ? Lgm_p2c2( f->E[i], LGM_Ee0 ) : LGM_FILL_VALUE; - fp = Lgm_DiffFluxToPsd( flux, p2c2 ); + + fp = Lgm_DiffFluxToPsd( flux, p2c2 ); f->PSD_EA[i][j] = fp; // PSD_EA is "PSD versus Energy and Pitch Angle". + + dfp = Lgm_DiffFluxToPsd( dflux, p2c2 ); + f->dPSD_EA[i][j] = dfp; // dPSD_EA is Uncertainty in PSD_EA + } } if ( f->DumpDiagnostics ) { DumpGif( "Lgm_FluxToPsd_PSD_EA", f->nA, f->nE, f->PSD_EA ); } - f->Alloced1 = TRUE; return; @@ -543,7 +586,7 @@ void Lgm_F2P_SetFlux( double **J, double *E, int nE, double *A, int nA, Lgm_Flux * and K. * * \details - * This routine ( Lgm_FluxPsd_GetPsdAtConstMusAndKs() ) must operate on a + * This routine ( Lgm_F2P_GetPsdAtConstMusAndKs() ) must operate on a * pre-initialized Lgm_FluxToPsd structure. The routine Lgm_FluxToPsd_SetFlux() * is used to add differential flux data/info to an Lgm_FluxToPsd structure * and it also converts the differential flux to Phase Space Density at @@ -584,9 +627,17 @@ void Lgm_F2P_SetFlux( double **J, double *E, int nE, double *A, int nA, Lgm_Flux */ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_MagModelInfo *mInfo, Lgm_FluxToPsd *f ) { - int k, m, DoIt; - double AlphaEq, SinA; - Lgm_MagModelInfo *mInfo2; + int i, j, k, m, DoIt, nGood, nGood2; + double AlphaEq, SqrtBrat, SinA, SinAeq, dPsd; + + int n, n_star, ngy_sum; + double xin[3000], yin[3000], dyin[3000]; + double x_star[300]; + int x_star_index[300]; + double gsig, gymax, gymin, gy_hat, gdy, gy_sum, gy_avg; + GprInfo *Info; + + Lgm_MagModelInfo *mInfo2; // This is used to hold thread-safe copies of mInfo. /* @@ -596,19 +647,30 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ LGM_ARRAY_1D_FREE( f->Mu ); LGM_ARRAY_1D_FREE( f->K ); LGM_ARRAY_1D_FREE( f->AofK ); + LGM_ARRAY_1D_FREE( f->AEqofK ); LGM_ARRAY_2D_FREE( f->EofMu ); + LGM_ARRAY_2D_FREE( f->PSD_EAeq ); + LGM_ARRAY_2D_FREE( f->dPSD_EAeq ); LGM_ARRAY_2D_FREE( f->PSD_MK ); + LGM_ARRAY_2D_FREE( f->dPSD_MK ); } /* - * Alloc arrays + * Alloc arrays, and flag that we have done this. */ - f->nMu = nMu; - f->nK = nK; - LGM_ARRAY_1D( f->Mu, f->nMu, double ); - LGM_ARRAY_1D( f->K, f->nK, double ); - LGM_ARRAY_1D( f->AofK, f->nK, double ); - LGM_ARRAY_2D( f->EofMu, f->nMu, f->nK, double ); + f->nMu = nMu; // Number of Mu values -- specified by user + f->nK = nK; // Number of K values -- specified by user + LGM_ARRAY_1D( f->Mu, f->nMu, double ); // Values for the Mu's -- user specified + LGM_ARRAY_1D( f->K, f->nK, double ); // Values for the K's -- user specified + LGM_ARRAY_1D( f->AofK, f->nK, double ); // Array to hold the Local Pitch Angle corresponding to the K's + LGM_ARRAY_1D( f->AEqofK, f->nK, double ); // Array to hold the Equatorial Pitch Angle corresponding to the K's + LGM_ARRAY_2D( f->EofMu, f->nMu, f->nK, double ); // 2D array holding the Energy that corresponds to a Mu,K pair + LGM_ARRAY_2D( f->PSD_EAeq, f->nE, f->nK, double ); // Phase Space Density as a function of E and Aeq + LGM_ARRAY_2D( f->dPSD_EAeq, f->nE, f->nK, double ); // Unc. in Phase Space Density as a function of E and Aeq + LGM_ARRAY_2D( f->PSD_MK, f->nMu, f->nK, double ); // Phase Space Density as a function of Mu and K + LGM_ARRAY_2D( f->dPSD_MK, f->nMu, f->nK, double ); // Unc. in Phase Space Density as a function of Mu and K + f->Alloced2 = TRUE; // Flag that e have alloced this mem. + /* @@ -619,9 +681,10 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ if ( Lgm_Setup_AlphaOfK( &(f->DateTime), &(f->Position), mInfo ) > 0 ) { - f->B = mInfo->Blocal; + f->B = mInfo->Blocal; + f->Beq = mInfo->Bmin; - { // start parallel + { // start openmp parallel execution #if USE_OPENMP #pragma omp parallel private(mInfo2,AlphaEq,SinA) @@ -634,6 +697,11 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ f->K[k] = K[k]; //printf("\n\nK[%d] = %g f->DateTime.UTC = %g f->Position = %g %g %g\n", k, K[k], f->DateTime.Time, f->Position.x, f->Position.y, f->Position.z); AlphaEq = Lgm_AlphaOfK( f->K[k], mInfo2 ); // Lgm_AlphaOfK() returns equatorial pitch angle. + + // Save the AlphaEq values + f->AEqofK[k] = AlphaEq; //These are the a_eq vals that correspond to our desired K values. + + SinA = sqrt( mInfo2->Blocal/mInfo2->Bmin ) * sin( RadPerDeg*AlphaEq ); if ( AlphaEq > 0.0 ) { if ( SinA <= 1.0 ) { @@ -657,14 +725,23 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ + Lgm_TearDown_AlphaOfK( mInfo ); } else { // Blocal will have been set in Lgm_Setup_AlphaOfK() even if it returned a value <= 0. f->B = mInfo->Blocal; - for ( k=0; kAofK[k] = LGM_FILL_VALUE; + for ( k=0; kAEqofK[k] = LGM_FILL_VALUE; + f->AofK[k] = LGM_FILL_VALUE; + } + + } + // how many good values did we get? + for ( nGood=0, k=0; kAEqofK[k] > 0.0 ) ++nGood; } if ( !(f->UseModelB) ) { @@ -672,6 +749,204 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ } + + + /* + * Create the Flux[E][a_eq] array using GPR interpolation. + * + * This was added Oct, 2020. + * The original algorithm was: + * 1) for each Mu and K, we figure out what E and Alpha_local we need. + * 2) Then the problem is just one of interpolating the PSD_EA array + * to get the PSD at the impplied E.Alpha_local. + * + * This works, but obviously, if the S/C is not at the Bmin point, it + * cannot see the lower K values (i.e. near 90-deg eq. pitch angles). In + * other words, if you just take the local PAD and re-label the pitch + * angles to their corresponding eq. pitch angles, a gap will open up + * around 90-deg. This is a PITA because even if the gap is small, this + * method is not ameniable to using interpolation, since the Alpha + * implied by a given K will be undefined (because Blocal>Bmin). + * + * This new algorithm changes this: + * 1) For each energy, the f( Alpha_local ) PAD is relabeled as f( + * Alpha_eq ) which typically opens a gap around 90. + * 2) Then feed this data into our Gaussian Process Regression Routine + * to interp to points at the AlphaEq implied by each K (i.e. instead + * of the Alpha_local's which may be undefined.) + */ + + + // For each local pitch angle, compute the equatorial pitch angle. + SqrtBrat = sqrt( mInfo->Bmin/mInfo->Blocal ); + for ( nGood2=0, j=0; jnA; j++ ) { + SinAeq = SqrtBrat * sin( RadPerDeg*f->A[j] ); + //if ( SinA <= 1.0 ) { + if ( SinAeq <= 1.0 ) { + f->Aeq[j] = DegPerRad*asin( SinAeq ); + ++nGood2; + } else { + f->Aeq[j] = LGM_FILL_VALUE; + } + } + + +//FILE *fp_pad, *fp_gpr; +//fp_pad=fopen("PAD.dat","w"); +//fp_gpr=fopen("GPR.dat","w"); + for ( i=0; inE; i++ ) { // for each energy + + // for each PAD + n = 0; gymax = -9e99; gymin = 9e99; + ngy_sum = 0; gy_sum = 0.0; + + /* + * Try adding mirror of first half of PAD. We are adding the main PAD + * in -90->90. But also adding: + * 1) the mirror of the first half is placed -180->-90 + * 2) the mirror of the last half is placed 90->180 + * 3) f=0 is forced at -90 and 90. With unc. of the 90-deg. val. + * CHECK ON THIS -- we may want to scan to find largest unc. + * available (90deg may be undefined.?) Note that GPR wont force the curve through zero! + */ + for ( j=f->nA/2; j>=0; j-- ) { + // add in mirror of first half of PAD + if ( !isnan(f->Aeq[j]) && (f->PSD_EA[i][j] > 0.0 ) && (f->PSD_EA[i][j] < 1e20 ) && (f->dPSD_EA[i][j] > 0.0) ){ + xin[n] = -f->Aeq[j] - 90.0; + yin[n] = f->PSD_EA[i][j]; + dyin[n] = f->dPSD_EA[i][j]; + ++n; + } + } + +// xin[n] = -90.0; +// yin[n] = 0.0; +// dyin[n] = f->dPSD_EA[i][f->nA/2]; // take unc. from 90deg PA. +// ++n; + + for ( j=0; jnA; j++ ) { // pitch angle bin + if ( !isnan(f->Aeq[j]) && (f->PSD_EA[i][j] > 0.0 ) && (f->PSD_EA[i][j] < 1e20 ) && (f->dPSD_EA[i][j] > 0.0) ){ + xin[n] = f->Aeq[j]-90.0; // Give it values -90 to 90 + yin[n] = f->PSD_EA[i][j]; + dyin[n] = f->dPSD_EA[i][j]; + if ( yin[n] > gymax ) gymax = yin[n]; + else if ( yin[n] < gymin ) gymin = yin[n]; + ++n; + } + } + for ( j=f->nA-1; j>=0; j-- ) { // pitch angle bin + if ( !isnan(f->Aeq[j]) && (f->PSD_EA[i][j] > 0.0 ) && (f->PSD_EA[i][j] < 1e20 ) && (f->dPSD_EA[i][j] > 0.0) ){ + xin[n] = 90.0-f->Aeq[j]; // Give it values 90 to 180 + yin[n] = f->PSD_EA[i][j]; + dyin[n] = f->dPSD_EA[i][j]; + if ( yin[n] > gymax ) gymax = yin[n]; + else if ( yin[n] < gymin ) gymin = yin[n]; + ++n; + } + } + +// xin[n] = 90.0; +// yin[n] = 0.0; +// dyin[n] = f->dPSD_EA[i][f->nA/2]; // take unc. from 90deg PA. +// ++n; + + // Try adding mirror of last half of PAD + for ( j=0; j<=f->nA/2; j++ ) { // pitch angle bin + if ( !isnan(f->Aeq[j]) && (f->PSD_EA[i][j] > 0.0 ) && (f->PSD_EA[i][j] < 1e20 ) && (f->dPSD_EA[i][j] > 0.0) ){ + xin[n] = 90.0+f->Aeq[j]; // Give it values 180 to 270 + yin[n] = f->PSD_EA[i][j]; + dyin[n] = f->dPSD_EA[i][j]; + ++n; + } + } +/* +for (j=0; jE[i], xin[j], yin[j], dyin[j], dyin[j]/yin[j]*100.0); +} +} +*/ +//fflush(fp_pad); + +//printf("E. Lgm_F2P_GetPsdAtConstMusAndKs %d %d\n", n, nGood); +//printf("n, nGood=%d %d\n", n, nGood); +//exit(0); + + // its possible that some of the AEqofK may not have computed correctly... + // so only use good values. But also remember what indices they are coming from + n_star = 0; + for (j=0; jnK; j++){ + if ( f->AEqofK[j] > 0.0 ) { + x_star[n_star] = f->AEqofK[j]-90.0; + x_star_index[n_star] = j; // remember what index this is from... + ++n_star; + } + } + + if ( (n>4)&&(nGood>2) ) { + /* + * OK, now we need to define the array of points at which we want to + * evaluate the function at. For visualization purposes, we could do + * this over a lot of regularly spaced points. But we really only need + * them at the f->AEqofK[k] points we calculated above. + */ + + + // Initialize the Info structure. + Info = InitGprInfo( n, n_star, 1 ); // The "1" means force symmetryo + for ( j=0; jx_star, j, x_star[j] ); + } + + for (j=0; jx, j, xin[j] ); + gsl_vector_set( Info->y, j, yin[j]/gymax - 0.5 ); + gsig = dyin[j]/gymax; + //printf("xin, yin = %g %g gsig = %g gymax = %g\n", xin[j], yin[j], gsig, gymax); + gsl_vector_set( Info->sigma_n_vec, j, gsig ); + gsl_vector_set( Info->sigma_n_2_vec, j, gsig*gsig ); + } + + // Do regression + //printf("i=%d n=%d\n", i, n); + GPR( Info ); + + + /* + * Save results. The FLUX_EAeq array should be interpolated to all of + * the required AlphaEq (corresponding to the Ks we want). + */ + for ( j=0; jnK; j++ ) f->PSD_EAeq[i][j] = LGM_FILL_VALUE; // Initialize to FILLs + for ( j=0; jy_hat, j ) +0.5)*gymax; + gdy = (gsl_vector_get( Info->y_cred_hi, j )+0.5)*gymax - gy_hat; +//printf("here gy_hat = %g gdy = %g\n", gy_hat, gdy); + f->PSD_EAeq[i][ x_star_index[j] ] = gy_hat; + f->dPSD_EAeq[i][ x_star_index[j] ] = gdy; + } + + + // Clean up memory + FreeGprInfo( Info ); + + } else { + for ( j=0; jPSD_EAeq[i][j] = LGM_FILL_VALUE; + f->dPSD_EAeq[i][j] = LGM_FILL_VALUE; + } + } +//printf("F. Lgm_F2P_GetPsdAtConstMusAndKs %d %d\n", n, nGood); +//exit(0); + + + } // end energy loop +//fclose(fp_pad); +//fclose(fp_gpr); +//exit(0); + + + /* * Copy Mu's (given in the arguments) into f structure. * Transform the Mu's into (Kinetic) Energies. @@ -679,27 +954,39 @@ void Lgm_F2P_GetPsdAtConstMusAndKs( double *Mu, int nMu, double *K, int nK, Lgm_ * Note that since this conversion involves Mu and Alpha, the result is 2D. assumes electrons -- generalize this... */ + for ( m=0; mMu[m] = Mu[m]; for ( k=0; kUseModelB ) { - f->EofMu[m][k] = Lgm_Mu_to_Ek( f->Mu[m], f->AofK[k], f->B, LGM_Ee0 ); + //f->EofMu[m][k] = Lgm_Mu_to_Ek( f->Mu[m], f->AofK[k], f->B, LGM_Ee0 ); + f->EofMu[m][k] = Lgm_Mu_to_Ek( f->Mu[m], f->AEqofK[k], f->Beq, LGM_Ee0 ); + } else { f->EofMu[m][k] = Lgm_Mu_to_Ek( f->Mu[m], f->AofK[k], f->B_obs, LGM_Ee0 ); } - //printf("f->Mu[%d], f->K[%d], f->AofK[%d], f->B, f->EofMu[%d][%d] = %g %g %g %g %g\n", m, k, k, m, k, f->Mu[m], f->K[k], f->AofK[k], f->B, f->EofMu[m][k]); + //printf("f->Mu[%d], f->K[%d], f->AofK[%d], f->B, f->EofMu[%d][%d] = %g %g %g %g %g\n", + // m, k, k, m, k, f->Mu[m], f->K[k], f->AofK[k], f->B, f->EofMu[m][k]); } } + + /* - * Now, from the PSD[E][a] array, get PSD at the E's and Alpha's we just computed. - * The result will be the same as PSD at the given Mu's and K's + * OLD ALGORITHM: + * Now, from the PSD[E][a] array, get PSD at the E's and Alpha's we just computed. + * The result will be the same as PSD at the given Mu's and K's + * + * NEW ALGORITHM: + * Grab the PSD from the PSD_EAeq array instead (that we just computed above). */ - LGM_ARRAY_2D( f->PSD_MK, f->nMu, f->nK, double ); for ( m=0; mEofMu[m][k] %g, f->E[0] %g \n", f->EofMu[m][k], f->E[0]); + //printf("f->EofMu[m][k] %g, f->E[0] %g \n", f->EofMu[m][k], f->E[0]); +f->Extrapolate=1; +f->Extrapolate=0; +f->Extrapolate=2; if ( f->Extrapolate > 2 ){ // extrapolate above and below DoIt = TRUE; @@ -720,25 +1007,33 @@ assumes electrons -- generalize this... } if (DoIt) { - f->PSD_MK[m][k] = Lgm_F2P_GetPsdAtEandAlpha( m, k, f->EofMu[m][k], f->AofK[k], f ); + + // This call is where the OLD interpolation would get done. + // Note that the one on pitch angle will no longer be needed.... + // The routine Lgm_F2P_GetPsdAtEandAlpha2() was added for this NEW way. + //f->PSD_MK[m][k] = Lgm_F2P_GetPsdAtEandAlpha( m, k, f->EofMu[m][k], f->AofK[k], &dPsd, f ); + f->PSD_MK[m][k] = Lgm_F2P_GetPsdAtEandAlpha2( m, f->EofMu[m][k], k, &dPsd, f ); + f->dPSD_MK[m][k] = dPsd; + } else { - f->PSD_MK[m][k] = LGM_FILL_VALUE; + + f->PSD_MK[m][k] = LGM_FILL_VALUE; + f->dPSD_MK[m][k] = LGM_FILL_VALUE; + } } } - if ( f->DumpDiagnostics ) { - DumpGif( "Lgm_FluxToPsd_PSD_MK", f->nK, f->nMu, f->PSD_MK ); - } - - - f->Alloced2 = TRUE; + if ( f->DumpDiagnostics ) { DumpGif( "Lgm_FluxToPsd_PSD_MK", f->nK, f->nMu, f->PSD_MK ); } return; } + + + double Model( double *x, int n, double E ) { int i; @@ -807,9 +1102,11 @@ if (isnan(sum)) { /** * The f structure should have an initialized PSD[E][a] array in it. - * This routine computes psd given a value of E and a. + * This routine computes psd given a value of E and a. dPsd is the uncertainty in Psd (return value). */ -double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_FluxToPsd *f ) { +double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, double *dpsd, Lgm_FluxToPsd *f ) { + +//NEED to get dPsd computed!!! int j, i, i0, i1, nn; double a0, a1, y0, y1, slp, psd, g; @@ -821,6 +1118,7 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux FitData = (_FitData *) calloc( 1, sizeof( _FitData ) ); FitData->nMaxwellians = f->nMaxwellians; +//FROM HERE /* * Since pitch angle, a is bounded (here its constrained to be between 0 * and 90), we will interpolate on that first to produce a 1D array of @@ -845,6 +1143,7 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux /************************ * interpolate PA + * For each energy, we interpolate the PA profile to get f at the user-specified alpha. ************************/ LGM_ARRAY_1D( FitData->E, f->nE, double ); LGM_ARRAY_1D( FitData->g, f->nE, double ); @@ -869,10 +1168,20 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux } +// TO HERE +//The above could just be replaced by picking out the right index to the f->PSD_EAeq[][] array now... +// Basically the g value is just read out of the array -- the indices are already at the right alpha val. +// E.g. +// g = f->PSD_EAeq[j][i] where i is given to us. I.e. we dont need all the crap to do the interp above... + + /* + * Now we have f versus E (at the specified alpha). + */ + if ( f->FitType == LGM_F2P_SPLINE ) { /* @@ -885,7 +1194,8 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux // determine number of points for (n=0, j=0; jn; ++j){ - if ( (f->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + //if ( (f->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + if ( (f->E[j] > 0.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { //printf("E[%d] = %g\n", j, f->E[j] ); ++n; } @@ -929,11 +1239,12 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux // set up data arrays. for ( nn=0, j=0; jn; j++ ){ - if ( (f->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { - xi = log10( FitData->E[j] ); - yi = log10( FitData->g[j] ); + //if ( (f->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + if ( (FitData->E[j] > 0.0/1000.0) && ( FitData->g[j] > 0.0 ) && ( FitData->dg[j] > 0.0 )) { + xi = log10( FitData->E[j] ); + yi = log10( FitData->g[j] ); + sigma = .434*( FitData->dg[j]/FitData->g[j] ); - sigma = 0.2*yi; // FIX -- i.e. need real values... gsl_vector_set( bs_x, nn, xi ); gsl_vector_set( bs_y, nn, yi ); gsl_vector_set( bs_w, nn, 1.0/(sigma*sigma) ); @@ -987,9 +1298,11 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux if ( (xi >= gsl_vector_get( bs_x, 0 )) && (xi <= gsl_vector_get( bs_x, n-1 )) ) { gsl_bspline_eval( xi, bs_B, bs_bw ); gsl_multifit_linear_est( bs_B, bs_c, bs_cov, &yi, &yierr ); - psd = pow( 10.0, yi ); + psd = pow( 10.0, yi ); + *dpsd = 2.303*psd*yierr; } else { - psd = LGM_FILL_VALUE; + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; } gsl_bspline_free( bs_bw ); @@ -1007,10 +1320,17 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux } else { psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; } + // spline fit to energy spectra is done. + + } else if ( f->FitType == LGM_F2P_MAXWELLIAN ) { +//Uncertainties are not propery set for thej maxwellian fits. +// Currently we use praxis -- a line minimization method, but we really should go to a differentm one +// in order to be able to get unc as well. if ( FitData->n > 2 ) { @@ -1064,22 +1384,322 @@ double Lgm_F2P_GetPsdAtEandAlpha( int iMu, int iK, double E, double a, Lgm_Flux //x[2] = 200.0; //printf("x - %g %g %g %g\n", x[1], x[2], x[3], x[4]); psd = Model( x, FitData->nMaxwellians, E ); +*dpsd = 0.0; +//dpsd = ?; //psd = (double)a; //printf("E, a = %g %g x = %g %g psd = %g\n", E, a, x[1], x[2], psd); + } else { + + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; + + } + + } else { + //FitType not valid... + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; + } + + LGM_ARRAY_1D_FREE( FitData->E ); + LGM_ARRAY_1D_FREE( FitData->g ); + free( FitData ); + + + return( psd ); + +} + + +/** + * This version uses the GP-interped array + * Here we just need to know what index to grab from the AlphaEq slot... + * The f structure should have an initialized PSD[E][a] array in it. + * This routine computes psd given a value of E and a. dPsd is the uncertainty in Psd (return value). + * + */ +double Lgm_F2P_GetPsdAtEandAlpha2( int iMu, double E, int iAEq, double *dpsd, Lgm_FluxToPsd *f ) { + + + int j, i, nn; + double psd, g, dg; + _FitData *FitData; + double MinEnergy, MaxEnergy; + + FitData = (_FitData *) calloc( 1, sizeof( _FitData ) ); + FitData->nMaxwellians = f->nMaxwellians; + + MinEnergy = 0.0/1000.0/1000.0; // 5eV in units of MeV +MinEnergy = 1.0; + MaxEnergy = 20.0; // MeV + + /************************ + * Grab the energy spectra at the given PA index + ************************/ + LGM_ARRAY_1D( FitData->E, f->nE, double ); + LGM_ARRAY_1D( FitData->g, f->nE, double ); + LGM_ARRAY_1D( FitData->dg, f->nE, double ); + FitData->n = 0; + for (j=0; jnE; ++j){ + if ( (f->E[j] > MinEnergy) && (f->E[j] <= MaxEnergy) && ( f->PSD_EAeq[j][iAEq] > 0.0 ) && (f->dPSD_EAeq[j][iAEq] > 0.0) ) { + g = f->PSD_EAeq[j][iAEq]; + dg = f->dPSD_EAeq[j][iAEq]; +// if ( g > 1e-40 ) { // dont use if it looks bogus + FitData->g[ FitData->n ] = g; + FitData->dg[ FitData->n ] = dg; + FitData->E[ FitData->n ] = f->E[j]; + ++(FitData->n); +//printf("A. %g %g %g\n", g, dg, f->E[j]); + // } + } + } + + + + /* + * Now we have f versus E (at the specified alpha). + */ + + if ( f->FitType == LGM_F2P_SPLINE ) { + + /* + * Use smoothing spline. + */ + int n; + //int ncoeffs = 12; + int ncoeffs = 8; + int nbreak = ncoeffs - 2; + + // determine number of points + for (n=0, j=0; jn; ++j){ + if ( (FitData->E[j] > MinEnergy) && (f->E[j] <= MaxEnergy) && ( FitData->g[j] > 0.0 ) && ( FitData->dg[j] > 0.0 )) { + //printf("E[%d] = %g g[%d] = %g\n", j, f->E[j], j, FitData->g[j] ); + ++n; + } + } + + if ( n > 20 ) { + ncoeffs = 12; + } else if ( n > 5 ) { + ncoeffs = 4; + } else { + ncoeffs = n/2; + } + nbreak = ncoeffs-2; + +//if ((iK==4)&&(iMu==7)) printf("n, ncoeffs, nbreak = %d %d %d\n", n, ncoeffs, nbreak ); + + if ( (n >= 4) && (nbreak>=2) ) { + + // allocate a cubic bspline workspace (k = 4) + gsl_bspline_workspace *bs_bw; + gsl_vector *bs_B; + bs_bw = gsl_bspline_alloc( 4, nbreak ); + bs_B = gsl_vector_alloc( ncoeffs ); + + + + gsl_vector *bs_x, *bs_y, *bs_c, *bs_w; + gsl_matrix *bs_X, *bs_cov; + gsl_multifit_linear_workspace *bs_mw; + double bs_chisq, xi, yi, yierr, sigma; + + + bs_x = gsl_vector_alloc( n ); + bs_y = gsl_vector_alloc( n ); + bs_X = gsl_matrix_alloc( n, ncoeffs ); + bs_c = gsl_vector_alloc( ncoeffs ); + bs_w = gsl_vector_alloc( n ); + bs_cov = gsl_matrix_alloc( ncoeffs, ncoeffs ); + bs_mw = gsl_multifit_linear_alloc( n, ncoeffs ); + + // set up data arrays. + for ( nn=0, j=0; jn; j++ ){ + + if ( (FitData->E[j] > MinEnergy) && (f->E[j] <= MaxEnergy) && ( FitData->g[j] > 0.0 ) && ( FitData->dg[j] > 0.0 )) { + xi = log10( FitData->E[j] ); + yi = log10( FitData->g[j] ); +if (FitData->dg[j]/FitData->g[j]*100.0 < 10.0){ + sigma = .0434; +} else { + sigma = .434*( FitData->dg[j]/FitData->g[j] ); +} + //sigma = 0.2*yi; +//printf("xi, yi, 0.2*yi, .434*( FitData->dg[j]/FitData->g[j] ) = %g %g %g %g g, dg = %g %g\n", xi, yi, 0.2*yi, .434*( FitData->dg[j]/FitData->g[j] ), FitData->g[j], FitData->dg[j]); +//printf("%g %g %g %g %g %g\n", xi, yi, sigma, FitData->E[j], FitData->g[j], FitData->dg[j]/FitData->g[j]); + + gsl_vector_set( bs_x, nn, xi ); + gsl_vector_set( bs_y, nn, yi ); + gsl_vector_set( bs_w, nn, 1.0/(sigma*sigma) ); + + ++nn; + } + + } + + // use uniform breakpoints on defined data interval + //printf("gsl_vector_get( bs_x, 0 ), gsl_vector_get( bs_x, n-1 ) = %g %g\n", gsl_vector_get( bs_x, 0 ), gsl_vector_get( bs_x, n-1 )); + gsl_bspline_knots_uniform( gsl_vector_get( bs_x, 0 ), gsl_vector_get( bs_x, n-1 ), bs_bw ); + //printf("B. here\n"); + + // construct the fit matrix X +//printf("n=%d nn=%d ", n, nn); +//printf("n, ncoeffs, nbreak = %d %d %d\n", n, ncoeffs, nbreak ); +//if (nn==92) FLAGFLAG = 1; +//if ((nn==93)&&(FLAGFLAG==1))exit(0); + for ( i=0; in; ++j){ + fprintf(fp, "%g %g\n", log10(f->E[j]), log10(FitData->g[j])); + } + fclose(fp); + fp = fopen("fit.txt", "w"); + for (xi = gsl_vector_get( bs_x, 0 ); xi < gsl_vector_get( bs_x, n-1 ); xi += 0.01) { + gsl_bspline_eval( xi, bs_B, bs_bw ); + gsl_multifit_linear_est( bs_B, bs_c, bs_cov, &yi, &yierr ); + fprintf(fp, "%g %g\n", xi, yi ); + } + fclose(fp); + exit(0); + */ + + xi = log10( E ); + + if ( (xi >= gsl_vector_get( bs_x, 0 )) && (xi <= gsl_vector_get( bs_x, n-1 )) ) { + gsl_bspline_eval( xi, bs_B, bs_bw ); + gsl_multifit_linear_est( bs_B, bs_c, bs_cov, &yi, &yierr ); + psd = pow( 10.0, yi ); + *dpsd = 2.303*psd*yierr; + +printf("E = %g xi = %g yi, yierr = %g %g psd, dpsd = %g %g\n", E, xi, yi, yierr, psd, *dpsd); + } else { + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; + } + + gsl_bspline_free( bs_bw ); + gsl_vector_free( bs_B ); + gsl_vector_free( bs_x ); + gsl_vector_free( bs_y ); + gsl_matrix_free( bs_X ); + gsl_vector_free( bs_c ); + gsl_vector_free( bs_w ); + gsl_matrix_free( bs_cov ); + gsl_multifit_linear_free( bs_mw ); + + + } else { psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; + + } + + // spline fit to energy spectra is done. + + + } else if ( f->FitType == LGM_F2P_MAXWELLIAN ) { +//Uncertainties are not propery set for thej maxwellian fits. +// Currently we use praxis -- a line minimization method, but we really should go to a differentm one +// in order to be able to get unc as well. + if ( FitData->n > 2 ) { + + + // interpolate/fit E + // for now just do a linear interp. + // no lets try a fit... + double in[10], out[7], x[10]; + in[0] = 1e-8; + in[1] = in[2] = 1e-9; //Info->Praxis_Tolerance; + in[5] = 30000.0; //(double)Info->Praxis_Max_Function_Evals; + in[6] = 10.0; //Info->Praxis_Maximum_Step_Size; + in[7] = 10.0; //Info->Praxis_Bad_Scale_Paramater; + in[8] = 4.0; //(double)Info->Praxis_Max_Its_Without_Improvement; + in[9] = 1.0; //(double)Info->Praxis_Ill_Conditioned_Problem; + x[0] = 0.0; + x[1] = -1.0; + x[2] = 25.0; + x[3] = -2.0; + x[4] = 200.0; + praxis( 2*FitData->nMaxwellians, x, (void *)FitData, Cost, in, out); + /* + printf("out[0] = %g\n", out[0]); + printf("out[1] = %g\n", out[1]); + printf("out[2] = %g\n", out[2]); + printf("out[3] = %g\n", out[3]); + printf("out[4] = %g\n", out[4]); + printf("out[5] = %g\n", out[5]); + printf("out[6] = %g\n", out[6]); + */ + //printf("x[1] = %g x[2] = %g Cost = %g\n", x[1], x[2], out[6]); + // FILE *fp; + // printf("E = %g\n", E); + // fp = fopen("data.txt", "w"); + // for (j=0; jn; ++j){ + // fprintf(fp, "%g %g\n", log10(f->E[j]), log10(FitData->g[j])); + // } + // fclose(fp); + + // fp = fopen("fit.txt", "w"); + // for (j=0; jn; ++j){ + // psd = Model( x, FitData->nMaxwellians, f->E[j] ); + // fprintf(fp, "%g %g\n", log10(f->E[j]), log10( psd ) ); + // } + // fclose(fp); + +// exit(0); + /* + */ + + + //x[2] = 200.0; + //printf("x - %g %g %g %g\n", x[1], x[2], x[3], x[4]); + psd = Model( x, FitData->nMaxwellians, E ); +*dpsd = 0.2*psd; +//dpsd = ?; + //psd = (double)a; + + //printf("E, a = %g %g x = %g %g psd = %g\n", E, a, x[1], x[2], psd); + } else { + + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; } } else { //FitType not valid... - psd = LGM_FILL_VALUE; + psd = LGM_FILL_VALUE; + *dpsd = LGM_FILL_VALUE; } LGM_ARRAY_1D_FREE( FitData->E ); LGM_ARRAY_1D_FREE( FitData->g ); + LGM_ARRAY_1D_FREE( FitData->dg ); free( FitData ); @@ -1540,8 +2160,9 @@ double Lgm_P2F_GetPsdAtMuAndK( double Mu, double K, double A, Lgm_PsdToFlux *p } // interpolate K - LGM_ARRAY_1D( FitData->E, p->nMu, double ); - LGM_ARRAY_1D( FitData->g, p->nMu, double ); + LGM_ARRAY_1D( FitData->E, p->nMu, double ); + LGM_ARRAY_1D( FitData->g, p->nMu, double ); + LGM_ARRAY_1D( FitData->dg, p->nMu, double ); FitData->n = 0; for (j=0; jnMu; ++j){ K0 = p->K[i0]; @@ -1570,7 +2191,8 @@ double Lgm_P2F_GetPsdAtMuAndK( double Mu, double K, double A, Lgm_PsdToFlux *p // determine number of points for (n=0, j=0; jn; ++j){ - if ( (FitData->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + //if ( (FitData->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + if ( (FitData->E[j] > 0.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { ++n; } } @@ -1608,11 +2230,12 @@ double Lgm_P2F_GetPsdAtMuAndK( double Mu, double K, double A, Lgm_PsdToFlux *p // set up data arrays. for ( nn=0, j=0; jn; j++ ){ - if ( (FitData->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { - xi = log10( FitData->E[j] ); - yi = log10( FitData->g[j] ); + //if ( (FitData->E[j] > 1.0/1000.0) && ( FitData->g[j] > 0.0 ) ) { + if ( (FitData->E[j] > 0.0/1000.0) && ( FitData->g[j] > 0.0 ) && ( FitData->dg[j] > 0.0 )) { + xi = log10( FitData->E[j] ); + yi = log10( FitData->g[j] ); + sigma = .434*( FitData->dg[j]/FitData->g[j] ); - sigma = 0.2*yi; // FIX -- i.e. need real values... gsl_vector_set( bs_x, nn, xi ); gsl_vector_set( bs_y, nn, yi ); gsl_vector_set( bs_w, nn, 1.0/(sigma*sigma) ); @@ -1715,6 +2338,7 @@ double Lgm_P2F_GetPsdAtMuAndK( double Mu, double K, double A, Lgm_PsdToFlux *p LGM_ARRAY_1D_FREE( FitData->E ); LGM_ARRAY_1D_FREE( FitData->g ); + LGM_ARRAY_1D_FREE( FitData->dg ); free( FitData ); diff --git a/libLanlGeoMag/Lgm_GradB.c b/libLanlGeoMag/Lgm_GradB.c index 860e3388c..4be9b0e16 100644 --- a/libLanlGeoMag/Lgm_GradB.c +++ b/libLanlGeoMag/Lgm_GradB.c @@ -5,7 +5,7 @@ /** * \brief - * Compute the gradient of B at a given point. + * Compute the gradient of |B-vec| at a given point. * * \details * Computes \f$ \nabla B \f$. @@ -94,6 +94,261 @@ void Lgm_GradB( Lgm_Vector *u0, Lgm_Vector *GradB, int DerivScheme, double h, Lg } +/** + * \brief + * Compute the d|B|/dcomp at a given point. + * + * \details + * Computes \f$ \partial B/\partial comp \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[in] comp seclects component (0=x, 1=y, 2=z) + * \param[out] dBdx The computed partial deriviative dB/dx at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_dBdcomp( Lgm_Vector *u0, int comp, double *dBdcomp, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double B, H, fc[7]; + int i, N; + Lgm_Vector u, Bvec; + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_dBdcomp: Computing dBdcomp for comp=%d with DerivScheme = %d, h = %g", comp, DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; + if ( comp == 0 ) { u.x += H; } + else if ( comp == 1 ) { u.y += H; } + else if ( comp == 2 ) { u.z += H; } + else { printf("\t\tLgm_dBdcomp: Invalid component. comp = %d (must be 0, 1, or 2).\n", comp); exit(0); } + m->Bfield( &u, &Bvec, m ); + B = Lgm_Magnitude( &Bvec ); + fc[i+N] = B; + } + } + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + *dBdcomp = (fc[6] - 9.0*fc[5] + 45.0*fc[4] - 45.0*fc[2] + 9.0*fc[1] - fc[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + *dBdcomp = (-fc[4] + 8.0*fc[3] - 8.0*fc[1] + fc[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + *dBdcomp = (fc[2] - fc[0])/(2.0*h); + } + if (m->VerbosityLevel > 0) printf(" dBdcomp = %g (component = %d)\n", *dBdcomp, comp); + + return; + +} +void Lgm_dBdx( Lgm_Vector *u0, double *dBdx, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dBdcomp( u0, 0, dBdx, DerivScheme, h, m ); } +void Lgm_dBdy( Lgm_Vector *u0, double *dBdy, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dBdcomp( u0, 1, dBdy, DerivScheme, h, m ); } +void Lgm_dBdz( Lgm_Vector *u0, double *dBdz, int DerivScheme, double h, Lgm_MagModelInfo *m ) { Lgm_dBdcomp( u0, 2, dBdz, DerivScheme, h, m ); } + + +/** + * \brief + * Compute the gradient of the diverence of b-hat at a given point. + * + * \details + * Computes \f$ \nabla \nabla\cdot \hat{b} \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Grad_Divb The computed gradient of Divb at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Grad_Divb( Lgm_Vector *u0, Lgm_Vector *Grad_Divb, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double Divb, H, fx[7], fy[7], fz[7]; + int i, N; + Lgm_Vector u; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_Divb( &u, &Divb, DerivScheme, h, m ); + fx[i+N] = Divb; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_Divb( &u, &Divb, DerivScheme, h, m ); + fy[i+N] = Divb; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_Divb( &u, &Divb, DerivScheme, h, m ); + fz[i+N] = Divb; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + Grad_Divb->x = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])/(60.0*h); + Grad_Divb->y = (fy[6] - 9.0*fy[5] + 45.0*fy[4] - 45.0*fy[2] + 9.0*fy[1] - fy[0])/(60.0*h); + Grad_Divb->z = (fz[6] - 9.0*fz[5] + 45.0*fz[4] - 45.0*fz[2] + 9.0*fz[1] - fz[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + Grad_Divb->x = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])/(12.0*h); + Grad_Divb->y = (-fy[4] + 8.0*fy[3] - 8.0*fy[1] + fy[0])/(12.0*h); + Grad_Divb->z = (-fz[4] + 8.0*fz[3] - 8.0*fz[1] + fz[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + Grad_Divb->x = (fx[2] - fx[0])/(2.0*h); + Grad_Divb->y = (fy[2] - fy[0])/(2.0*h); + Grad_Divb->z = (fz[2] - fz[0])/(2.0*h); + } + if (m->VerbosityLevel > 0) printf(" Grad_Divb = (%g %g %g)\n", Grad_Divb->x, Grad_Divb->y, Grad_Divb->z ); + + return; + +} + + +/** + * \brief + * Compute the Laplacian of |B| at a given point. + * + * \details + * Computes \f$ \nabla^2 |B| \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] LaplacianB The computed gradient of Divb at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_LaplacianB( Lgm_Vector *u0, double *LaplacianB, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, d2Bdx2, d2Bdy2, d2Bdz2; + double dBdx, dBdy, dBdz; + double fx[7], fy[7], fz[7]; + double gx[7], gy[7], gz[7]; + double hx[7], hy[7], hz[7]; + int i, N; + Lgm_Vector u; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + Lgm_dBdx( &u, &dBdx, DerivScheme, h, m ); + fx[i+N] = dBdx; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + Lgm_dBdy( &u, &dBdy, DerivScheme, h, m ); + gy[i+N] = dBdy; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + Lgm_dBdz( &u, &dBdz, DerivScheme, h, m ); + hz[i+N] = dBdz; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + d2Bdx2 = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])/(60.0*h); + d2Bdy2 = (gy[6] - 9.0*gy[5] + 45.0*gy[4] - 45.0*gy[2] + 9.0*gy[1] - gy[0])/(60.0*h); + d2Bdz2 = (hz[6] - 9.0*hz[5] + 45.0*hz[4] - 45.0*hz[2] + 9.0*hz[1] - hz[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + d2Bdx2 = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])/(12.0*h); + d2Bdy2 = (-gy[4] + 8.0*gy[3] - 8.0*gy[1] + gy[0])/(12.0*h); + d2Bdz2 = (-hz[4] + 8.0*hz[3] - 8.0*hz[1] + hz[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + d2Bdx2 = (hz[2] - hz[0])/(2.0*h); + d2Bdy2 = (hz[2] - hz[0])/(2.0*h); + d2Bdz2 = (hz[2] - hz[0])/(2.0*h); + } + *LaplacianB = d2Bdx2 + d2Bdy2 + d2Bdz2; + if (m->VerbosityLevel > 0) printf(" LaplacianB = %g\n", *LaplacianB ); + + return; + +} + + /** * \brief * Compute the gradient of B and it's parallel and perpendicular components at a given point. @@ -159,14 +414,14 @@ void Lgm_B_Cross_GradB_Over_B( Lgm_Vector *u0, Lgm_Vector *A, int DerivScheme, d /** * \brief - * Compute the curl of B at a given point. + * Compute the curl of B-vec at a given point. * * \details * Computes \f$ \nabla\times B \f$. * * * \param[in] u0 Position (in GSM) to use. - * \param[out] GradB The computed curl of B at position u0. + * \param[out] CurlB The computed curl of B at position u0. * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. @@ -293,6 +548,149 @@ void Lgm_CurlB( Lgm_Vector *u0, Lgm_Vector *CurlB, int DerivScheme, double h, Lg } + +/** + * \brief + * Compute the curl of b-hat at a given point. + * + * \details + * Computes \f$ \nabla\times b \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] CurlB The computed curl of B at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2011 + * + */ +void Lgm_Curlb( Lgm_Vector *u0, Lgm_Vector *Curlb, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double Bmag, H; + double fxy[7], fxz[7], fyx[7], fyz[7], fzx[7], fzy[7]; + double dbxdy, dbxdz, dbydx, dbydz, dbzdx, dbzdy; + int i, N; + Lgm_Vector u, Bvec; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_Curlb: Computing Curlb with DerivScheme = %d, h = %g\n", DerivScheme, h); + // dbx/dy and dbx/dz + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fxy[i+N] = Bvec.x/Bmag; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fxz[i+N] = Bvec.x/Bmag; + } + } + + // dby/dx and dby/dz + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fyx[i+N] = Bvec.y/Bmag; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fyz[i+N] = Bvec.y/Bmag; + } + } + + // dbz/dx and dbz/dy + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fzx[i+N] = Bvec.z/Bmag; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + m->Bfield( &u, &Bvec, m ); + Bmag = Lgm_Magnitude( &Bvec ); + fzy[i+N] = Bvec.z/Bmag; + } + } + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + dbxdy = (fxy[6] - 9.0*fxy[5] + 45.0*fxy[4] - 45.0*fxy[2] + 9.0*fxy[1] - fxy[0])/(60.0*h); + dbxdz = (fxz[6] - 9.0*fxz[5] + 45.0*fxz[4] - 45.0*fxz[2] + 9.0*fxz[1] - fxz[0])/(60.0*h); + + dbydx = (fyx[6] - 9.0*fyx[5] + 45.0*fyx[4] - 45.0*fyx[2] + 9.0*fyx[1] - fyx[0])/(60.0*h); + dbydz = (fyz[6] - 9.0*fyz[5] + 45.0*fyz[4] - 45.0*fyz[2] + 9.0*fyz[1] - fyz[0])/(60.0*h); + + dbzdx = (fzx[6] - 9.0*fzx[5] + 45.0*fzx[4] - 45.0*fzx[2] + 9.0*fzx[1] - fzx[0])/(60.0*h); + dbzdy = (fzy[6] - 9.0*fzy[5] + 45.0*fzy[4] - 45.0*fzy[2] + 9.0*fzy[1] - fzy[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + dbxdy = (-fxy[4] + 8.0*fxy[3] - 8.0*fxy[1] + fxy[0])/(12.0*h); + dbxdz = (-fxz[4] + 8.0*fxz[3] - 8.0*fxz[1] + fxz[0])/(12.0*h); + + dbydx = (-fyx[4] + 8.0*fyx[3] - 8.0*fyx[1] + fyx[0])/(12.0*h); + dbydz = (-fyz[4] + 8.0*fyz[3] - 8.0*fyz[1] + fyz[0])/(12.0*h); + + dbzdx = (-fzx[4] + 8.0*fzx[3] - 8.0*fzx[1] + fzx[0])/(12.0*h); + dbzdy = (-fzy[4] + 8.0*fzy[3] - 8.0*fzy[1] + fzy[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + dbxdy = (fxy[2] - fxy[0])/(2.0*h); + dbxdz = (fxz[2] - fxz[0])/(2.0*h); + + dbydx = (fyx[2] - fyx[0])/(2.0*h); + dbydz = (fyz[2] - fyz[0])/(2.0*h); + + dbzdx = (fzx[2] - fzx[0])/(2.0*h); + dbzdy = (fzy[2] - fzy[0])/(2.0*h); + } + Curlb->x = dbzdy - dbydz; + Curlb->y = dbxdz - dbzdx; + Curlb->z = dbydx - dbxdy; +//printf("NRM dbdx = %g %g %g\n", -999.0, dbydx, dbzdx ); +//printf("NRM dbdy = %g %g %g\n", dbxdy, -999.0, dbzdy ); +//printf("NRM dbdz = %g %g %g\n", dbxdz, dbydz, -999.0 ); + if (m->VerbosityLevel > 0) printf(" Curlb = (%g %g %g)\n", Curlb->x, Curlb->y, Curlb->z ); + + return; + +} + + /** * \brief * Compute the curl of B and it's parallel and perpendicular components at a given point. @@ -441,6 +839,107 @@ void Lgm_DivB( Lgm_Vector *u0, double *DivB, int DerivScheme, double h, Lgm_MagM } +/** + * \brief + * Compute the divergence of b-hat at a given point. + * + * \details + * Computes \f$ \nabla\cdot \hat{b} \f$. Note that while by Maxwell's + * equations, \f$ \nabla\cdot \vec{B} \f$ should be zero, the divergence of + * the b-field unit vector is not. This quantity is needed by some + * calculations (e.g. calculating higher order terms in the first + * adiabatic invariant ala Burby et al. 2013.) + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] Divb The computed divergence of b-hat at position u0. + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Divb( Lgm_Vector *u0, double *Divb, int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double B, H; + double fxx[7], fyy[7], fzz[7]; + double dbxdx, dbydy, dbzdz; + int i, N; + Lgm_Vector u, Bvec; + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_DivB: Computing DivB with DerivScheme = %d, h = %g", DerivScheme, h); + // dBx/dx + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; + m->Bfield( &u, &Bvec, m ); + Lgm_NormalizeVector( &Bvec ); + fxx[i+N] = Bvec.x; + } + } + + // dBy/dy + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; + m->Bfield( &u, &Bvec, m ); + Lgm_NormalizeVector( &Bvec ); + fyy[i+N] = Bvec.y; + } + } + + // dBz/dz + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; + m->Bfield( &u, &Bvec, m ); + Lgm_NormalizeVector( &Bvec ); + fzz[i+N] = Bvec.z; + } + } + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + dbxdx = (fxx[6] - 9.0*fxx[5] + 45.0*fxx[4] - 45.0*fxx[2] + 9.0*fxx[1] - fxx[0])/(60.0*h); + dbydy = (fyy[6] - 9.0*fyy[5] + 45.0*fyy[4] - 45.0*fyy[2] + 9.0*fyy[1] - fyy[0])/(60.0*h); + dbzdz = (fzz[6] - 9.0*fzz[5] + 45.0*fzz[4] - 45.0*fzz[2] + 9.0*fzz[1] - fzz[0])/(60.0*h); + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + dbxdx = (-fxx[4] + 8.0*fxx[3] - 8.0*fxx[1] + fxx[0])/(12.0*h); + dbydy = (-fyy[4] + 8.0*fyy[3] - 8.0*fyy[1] + fyy[0])/(12.0*h); + dbzdz = (-fzz[4] + 8.0*fzz[3] - 8.0*fzz[1] + fzz[0])/(12.0*h); + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + dbxdx = (fxx[2] - fxx[0])/(2.0*h); + dbydy = (fyy[2] - fyy[0])/(2.0*h); + dbzdz = (fzz[2] - fzz[0])/(2.0*h); + } + *Divb = dbxdx + dbydy + dbzdz; + if (m->VerbosityLevel > 0) printf(" dbxdx, dbydy, dbzdz, Divb = %g %g %g %g\n", dbxdx, dbydy, dbzdz, *Divb ); + + return; + +} + /** @@ -456,7 +955,7 @@ void Lgm_DivB( Lgm_Vector *u0, double *DivB, int DerivScheme, double h, Lgm_MagM * 2B_m}\right){\vec{B}\times\nabla B\over B} + \left(1-{B\over * B_m}\right){(\nabla\times \vec{B})_perp} \right) * \f] - * (e.g. see Sukhtina, M.A., "One the calculation of the magnetic srift + * (e.g. see Sukhtina, M.A., "On the calculation of the magnetic drift * velocity of particles with arbitrary pitch angles, Planet Space Sci. * 41, 327--331, 1993.) With \f$\eta = T/E_0\f$, where \f$T\f$ is the * particle's kinetic energy, and \f$E_0\f$ its rest energy, this can be @@ -567,3 +1066,280 @@ int Lgm_GradAndCurvDriftVel( Lgm_Vector *u0, Lgm_Vector *Vel, Lgm_MagModelInfo * return(1); } + + +/** + * \brief + * Compute the field radius of curvature vector. + * + * \details + * The gradient and curvature drift velocity. + * + * + * \param[in] u0 Position (in GSM) to use. [Re] + * \param[out] Rc The radius of curvature vector. [Re] + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_Curvature( Lgm_Vector *q, Lgm_Vector *dbds, int DerivScheme, double h, Lgm_MagModelInfo *m ){ + + double H, Htry, Hdid, Hnext, sgn, s, fs[7], gs[7], hs[7]; + int i, N, reset; + Lgm_Vector u, u_scale, b; + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + u_scale.x = u_scale.y = u_scale.z = 1.0; + if (m->VerbosityLevel > 0) printf("\t\tLgm_Curvature: Computing Curvature of B with DerivScheme = %d, h = %g", DerivScheme, h); + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = *q; H = (double)i*h; + reset = TRUE; + if ( H < 0.0 ) { Htry = -H; sgn = -1.0; } else { Htry = H; sgn = 1.0; } + Lgm_MagStep( &u, &u_scale, Htry, &Hdid, &Hnext, sgn, &s, &reset, m->Bfield, m ); + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fs[i+N] = b.x; //Re^-1 + gs[i+N] = b.y; //Re^-1 + hs[i+N] = b.z; //Re^-1 + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + dbds->x = (fs[6] - 9.0*fs[5] + 45.0*fs[4] - 45.0*fs[2] + 9.0*fs[1] - fs[0])/(60.0*h); // Re^-1 + dbds->y = (gs[6] - 9.0*gs[5] + 45.0*gs[4] - 45.0*gs[2] + 9.0*gs[1] - gs[0])/(60.0*h); // Re^-1 + dbds->z = (hs[6] - 9.0*hs[5] + 45.0*hs[4] - 45.0*hs[2] + 9.0*hs[1] - hs[0])/(60.0*h); // Re^-1 + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + dbds->x = (-fs[4] + 8.0*fs[3] - 8.0*fs[1] + fs[0])/(12.0*h); // Re^-1 + dbds->y = (-gs[4] + 8.0*gs[3] - 8.0*gs[1] + gs[0])/(12.0*h); // Re^-1 + dbds->z = (-hs[4] + 8.0*hs[3] - 8.0*hs[1] + hs[0])/(12.0*h); // Re^-1 + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + dbds->x = (fs[2] - fs[0])/(2.0*h); // Re^-1 + dbds->y = (gs[2] - gs[0])/(2.0*h); // Re^-1 + dbds->z = (hs[2] - hs[0])/(2.0*h); // Re^-1 + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + + return; + +} + +void Lgm_Curvature2( Lgm_Vector *q, Lgm_Vector *dbds, int DerivScheme, double h, Lgm_MagModelInfo *m ){ + + double H, Htry, Hdid, Hnext, sgn, s, fs[7], gs[7], hs[7]; + double fxx[7], fxy[7], fxz[7]; + double fyx[7], fyy[7], fyz[7]; + double fzx[7], fzy[7], fzz[7]; + double dbx_dx, dby_dx, dbz_dx, dbx_dy, dby_dy, dbz_dy, dbx_dz, dby_dz, dbz_dz; + int i, N, reset; + Lgm_Vector u0, u, u_scale, b; + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + default: + N = 3; + } + + if (m->VerbosityLevel > 0) printf("\t\tLgm_Curvature: Computing Curvature of B with DerivScheme = %d, h = %g", DerivScheme, h); + + // x derivs + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = *q; H = (double)i*h; u.x += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fxx[i+N] = b.x; //Re^-1 + fyx[i+N] = b.y; //Re^-1 + fzx[i+N] = b.z; //Re^-1 + } + } + // y derivs + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = *q; H = (double)i*h; u.y += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fxy[i+N] = b.x; //Re^-1 + fyy[i+N] = b.y; //Re^-1 + fzy[i+N] = b.z; //Re^-1 + } + } + // z derivs + for (i=-N; i<=N; ++i){ + if ( i != 0 ) { + u = *q; H = (double)i*h; u.z += H; + m->Bfield( &u, &b, m ); + Lgm_NormalizeVector( &b ); + fxz[i+N] = b.x; //Re^-1 + fyz[i+N] = b.y; //Re^-1 + fzz[i+N] = b.z; //Re^-1 + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + dbx_dx = (fxx[6] - 9.0*fxx[5] + 45.0*fxx[4] - 45.0*fxx[2] + 9.0*fxx[1] - fxx[0])/(60.0*h); // Re^-1 + dby_dx = (fyx[6] - 9.0*fyx[5] + 45.0*fyx[4] - 45.0*fyx[2] + 9.0*fyx[1] - fyx[0])/(60.0*h); // Re^-1 + dbz_dx = (fzx[6] - 9.0*fzx[5] + 45.0*fzx[4] - 45.0*fzx[2] + 9.0*fzx[1] - fzx[0])/(60.0*h); // Re^-1 + dbx_dy = (fxy[6] - 9.0*fxy[5] + 45.0*fxy[4] - 45.0*fxy[2] + 9.0*fxy[1] - fxy[0])/(60.0*h); // Re^-1 + dby_dy = (fyy[6] - 9.0*fyy[5] + 45.0*fyy[4] - 45.0*fyy[2] + 9.0*fyy[1] - fyy[0])/(60.0*h); // Re^-1 + dbz_dy = (fzy[6] - 9.0*fzy[5] + 45.0*fzy[4] - 45.0*fzy[2] + 9.0*fzy[1] - fzy[0])/(60.0*h); // Re^-1 + dbx_dz = (fxz[6] - 9.0*fxz[5] + 45.0*fxz[4] - 45.0*fxz[2] + 9.0*fxz[1] - fxz[0])/(60.0*h); // Re^-1 + dby_dz = (fyz[6] - 9.0*fyz[5] + 45.0*fyz[4] - 45.0*fyz[2] + 9.0*fyz[1] - fyz[0])/(60.0*h); // Re^-1 + dbz_dz = (fzz[6] - 9.0*fzz[5] + 45.0*fzz[4] - 45.0*fzz[2] + 9.0*fzz[1] - fzz[0])/(60.0*h); // Re^-1 + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + dbx_dx = (-fxx[4] + 8.0*fxx[3] - 8.0*fxx[1] + fxx[0])/(12.0*h); // Re^-1 + dby_dx = (-fyx[4] + 8.0*fyx[3] - 8.0*fyx[1] + fyx[0])/(12.0*h); // Re^-1 + dbz_dx = (-fzx[4] + 8.0*fzx[3] - 8.0*fzx[1] + fzx[0])/(12.0*h); // Re^-1 + dbx_dy = (-fxy[4] + 8.0*fxy[3] - 8.0*fxy[1] + fxy[0])/(12.0*h); // Re^-1 + dby_dy = (-fyy[4] + 8.0*fyy[3] - 8.0*fyy[1] + fyy[0])/(12.0*h); // Re^-1 + dbz_dy = (-fzy[4] + 8.0*fzy[3] - 8.0*fzy[1] + fzy[0])/(12.0*h); // Re^-1 + dbx_dz = (-fxz[4] + 8.0*fxz[3] - 8.0*fxz[1] + fxz[0])/(12.0*h); // Re^-1 + dby_dz = (-fyz[4] + 8.0*fyz[3] - 8.0*fyz[1] + fyz[0])/(12.0*h); // Re^-1 + dbz_dz = (-fzz[4] + 8.0*fzz[3] - 8.0*fzz[1] + fzz[0])/(12.0*h); // Re^-1 + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + dbx_dx = (fxx[2] - fxx[0])/(2.0*h); // Re^-1 + dby_dx = (fyx[2] - fyx[0])/(2.0*h); // Re^-1 + dbz_dx = (fzx[2] - fzx[0])/(2.0*h); // Re^-1 + dbx_dy = (fxy[2] - fxy[0])/(2.0*h); // Re^-1 + dby_dy = (fyy[2] - fyy[0])/(2.0*h); // Re^-1 + dbz_dy = (fzy[2] - fzy[0])/(2.0*h); // Re^-1 + dbx_dz = (fxz[2] - fxz[0])/(2.0*h); // Re^-1 + dby_dz = (fyz[2] - fyz[0])/(2.0*h); // Re^-1 + dbz_dz = (fzz[2] - fzz[0])/(2.0*h); // Re^-1 + } +// if (m->VerbosityLevel > 0) printf(" GradB = (%g %g %g)\n", GradB->x, GradB->y, GradB->z ); + + m->Bfield( q, &b, m ); + Lgm_NormalizeVector( &b ); + dbds->x = b.x * dbx_dx + b.y * dbx_dy + b.z * dbx_dz; + dbds->y = b.x * dby_dx + b.y * dby_dy + b.z * dby_dz; + dbds->z = b.x * dbz_dx + b.y * dbz_dy + b.z * dbz_dz; + + return; + +} + + + + + + +/** + * \brief + * Compute the instantaneous gradient and curvature drift velocity of the guiding center. + * + * \details + * The gradient and curvature drift velocity of the guiding center is given by; + * + * + * \param[in] u0 Position (in GSM) to use. [Re] + * \param[in] v Total velocity (in GSM) to use. [m/s] + * \param[out] v_GradB The computed GradB drift velocity in GSM coords. [m/s] + * \param[out] v_CurvB The computed Curvature drift velocity in GSM coords. [m/s] + * \param[out] v_CurvB The computed Curvature drift velocity in GSM coords. [m/s] + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +int Lgm_Vdrift_GradB_Curv( Lgm_Vector *u0, Lgm_Vector *v_full, double Mass, double Charge, Lgm_Vector *v_GradB, Lgm_Vector *v_CurvB, Lgm_Vector *Rc_vec, Lgm_MagModelInfo *m ) { + + double Gamma; + double q, T, E0, Bm; + Lgm_Vector Kappa, B_vec, GradB; + double K, Rc, Rc2, B, B2, B3, u, u2, v, v2, w2, g; + + + m->Bfield( u0, &B_vec, m ); + B_vec.x *= 1e-9; B_vec.y *= 1e-9; B_vec.z *= 1e-9; // T + B = Lgm_Magnitude( &B_vec ); + B2 = B*B; B3 = B2*B; + + v = Lgm_Magnitude( v_full ); // speed in m/s + v2 = v*v; + u = Lgm_DotProduct( &B_vec, v_full)/B; // u_parallel in m/s + u2 = u*u; + w2 = v2 - u2; // u_perp^2 in m^2/s^2 + Gamma = 1.0/sqrt( 1.0 - v2/(LGM_c*LGM_c) ); + +Lgm_Vector dbds; + Lgm_Curvature( u0, &dbds, LGM_DERIV_SIX_POINT, 1e-4, m); + g = 1.0/(Re*1e3); dbds.x *= g; dbds.y *= g; dbds.z *= g; +// K = Lgm_Magnitude( &Kappa ); + // Rc = 1.0/K; +// Rc2 = Rc*Rc; +//printf("Lgm_Vdrift_GradB_Curv: Rc = %g\n", Rc); +// *Rc_vec = Kappa; + +// Lgm_NormalizeVector( Rc_vec ); +// Lgm_ScaleVector( Rc_vec, -Rc ); + +//Lgm_Vector Rc_vec_over_Rc2; +//Rc_vec_over_Rc2.x = -dbds.x; +//Rc_vec_over_Rc2.y = -dbds.y; +//Rc_vec_over_Rc2.z = -dbds.z; +// Lgm_CrossProduct( &Rc_vec_over_Rc2, &B_vec, v_CurvB ); +Lgm_CrossProduct( &B_vec, &dbds, v_CurvB ); + + Lgm_ScaleVector( v_CurvB, Gamma*Mass*u2/(Charge*B2) ); + + + + + + Lgm_GradB( u0, &GradB, LGM_DERIV_SIX_POINT, 1e-4, m ); + g = 1e-9/(Re*1e3); GradB.x *= g; GradB.y *= g; GradB.z *= g; // T/m + Lgm_CrossProduct( &B_vec, &GradB, v_GradB ); + Lgm_ScaleVector( v_GradB, Gamma*Mass*w2/(2.0*Charge*B3) ); + + Lgm_ScaleVector( Rc_vec, Rc ); + +//printf("u0 = %g %g %g\n", u0->x, u0->y, u0->z ); +//printf("v_GradB = %g %g %g\n", v_GradB->x, v_GradB->y, v_GradB->z ); +//printf("v_CurvB = %g %g %g\n", v_CurvB->x, v_CurvB->y, v_CurvB->z ); + + return(1); + +} diff --git a/libLanlGeoMag/Lgm_GradBvec.c b/libLanlGeoMag/Lgm_GradBvec.c new file mode 100644 index 000000000..c98c7b459 --- /dev/null +++ b/libLanlGeoMag/Lgm_GradBvec.c @@ -0,0 +1,205 @@ +#include +#include +#include "Lgm/Lgm_MagModelInfo.h" + +/** + * \brief + * Compute the gradient of B-vec at a given point. + * + * \details + * Computes \f$ \nabla B \f$. + * + * + * \param[in] u0 Position (in GSM) to use. + * \param[out] GradBvec The computed gradient of Bvec at position u0 -- note: this is a tensor + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in,out] m A properly initialized and configured Lgm_MagModelInfo structure. + * + * + * \author Mike Henderson + * \date 2023 + * + */ +void Lgm_GradBvec( Lgm_Vector *u0, double GradBvec[3][3], int DerivScheme, double h, Lgm_MagModelInfo *m ) { + + double H, s; + double fx[7], fy[7], fz[7]; + double gx[7], gy[7], gz[7]; + double qx[7], qy[7], qz[7]; + int i, N; + double dbx_dx, dbx_dy, dbx_dz; + double dby_dx, dby_dy, dby_dz; + double dbz_dx, dbz_dy, dbz_dz; + Lgm_Vector u, Bvec; + + + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + + /* + * Compute all first order derivatives of magnetic field components. + * I.e., using subscript notation for derivs, we need these quantities: + * bx_t, bx_x, bx_y, bx_z + * by_t, by_x, by_y, by_z + * bz_t, bz_x, bz_y, bz_z + */ + if (m->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + + /* + * Bx Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m->Bfield( &u, &Bvec, m ); + fx[i+N] = Bvec.x; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m->Bfield( &u, &Bvec, m ); + fy[i+N] = Bvec.x; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m->Bfield( &u, &Bvec, m ); + fz[i+N] = Bvec.x; + } + } + + /* + * By Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m->Bfield( &u, &Bvec, m ); + gx[i+N] = Bvec.y; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m->Bfield( &u, &Bvec, m ); + gy[i+N] = Bvec.y; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m->Bfield( &u, &Bvec, m ); + gz[i+N] = Bvec.y; + } + } + + + /* + * Bz Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m->Bfield( &u, &Bvec, m ); + qx[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m->Bfield( &u, &Bvec, m ); + qy[i+N] = Bvec.z; + } + }printf(" = (%8g %8g %8g)\n", dbx_dx, dbx_dy, dbx_dz ); + printf(" Grad Bvec = (%8g %8g %8g)\n", dby_dx, dby_dy, dby_dz ); + printf(" = (%8g %8g %8g)\n", dbz_dx, dbz_dy, dbz_dz ); + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m->Bfield( &u, &Bvec, m ); + qz[i+N] = Bvec.z; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + + s = 1.0/(60.0*h); + + dbx_dx = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])*s; + dbx_dy = (fy[6] - 9.0*fy[5] + 45.0*fy[4] - 45.0*fy[2] + 9.0*fy[1] - fy[0])*s; + dbx_dz = (fz[6] - 9.0*fz[5] + 45.0*fz[4] - 45.0*fz[2] + 9.0*fz[1] - fz[0])*s; + + dby_dx = (gx[6] - 9.0*gx[5] + 45.0*gx[4] - 45.0*gx[2] + 9.0*gx[1] - gx[0])*s; + dby_dy = (gy[6] - 9.0*gy[5] + 45.0*gy[4] - 45.0*gy[2] + 9.0*gy[1] - gy[0])*s; + dby_dz = (gz[6] - 9.0*gz[5] + 45.0*gz[4] - 45.0*gz[2] + 9.0*gz[1] - gz[0])*s; + + dbz_dx = (qx[6] - 9.0*qx[5] + 45.0*qx[4] - 45.0*qx[2] + 9.0*qx[1] - qx[0])*s; + dbz_dy = (qy[6] - 9.0*qy[5] + 45.0*qy[4] - 45.0*qy[2] + 9.0*qy[1] - qy[0])*s; + dbz_dz = (qz[6] - 9.0*qz[5] + 45.0*qz[4] - 45.0*qz[2] + 9.0*qz[1] - qz[0])*s; + + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + + s = 1.0/(12.0*h); + + dbx_dx = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])/(12.0*h); + dbx_dy = (-fy[4] + 8.0*fy[3] - 8.0*fy[1] + fy[0])/(12.0*h); + dbx_dz = (-fz[4] + 8.0*fz[3] - 8.0*fz[1] + fz[0])/(12.0*h); + + dby_dx = (-gx[4] + 8.0*gx[3] - 8.0*gx[1] + gx[0])*s; + dby_dy = (-gy[4] + 8.0*gy[3] - 8.0*gy[1] + gy[0])*s; + dby_dz = (-gz[4] + 8.0*gz[3] - 8.0*gz[1] + gz[0])*s; + + dbz_dx = (-qx[4] + 8.0*qx[3] - 8.0*qx[1] + qx[0])*s; + dbz_dy = (-qy[4] + 8.0*qy[3] - 8.0*qy[1] + qy[0])*s; + dbz_dz = (-qz[4] + 8.0*qz[3] - 8.0*qz[1] + qz[0])*s; + + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + + s = 1.0/(2.0*h); + dbx_dx = (fx[2] - fx[0])*s; + dbx_dy = (fy[2] - fy[0])*s; + dbx_dz = (fz[2] - fz[0])*s; + + dby_dx = (gx[2] - gx[0])*s; + dby_dy = (gy[2] - gy[0])*s; + dby_dz = (gz[2] - gz[0])*s; + + dbz_dx = (qx[2] - qx[0])*s; + dbz_dy = (qy[2] - qy[0])*s; + dbz_dz = (qz[2] - qz[0])*s; + + } + if (m->VerbosityLevel > 0) { + printf(" = (%8g %8g %8g)\n", dbx_dx, dbx_dy, dbx_dz ); + printf(" Grad Bvec = (%8g %8g %8g)\n", dby_dx, dby_dy, dby_dz ); + printf(" = (%8g %8g %8g)\n", dbz_dx, dbz_dy, dbz_dz ); + } + + GradBvec[0][0] = dbx_dx; GradBvec[0][1] = dbx_dy; GradBvec[0][2] = dbx_dz; + GradBvec[1][0] = dby_dx; GradBvec[1][1] = dby_dy; GradBvec[1][2] = dby_dz; + GradBvec[2][0] = dbz_dx; GradBvec[2][1] = dbz_dy; GradBvec[2][2] = dbz_dz; + + return; + +} diff --git a/libLanlGeoMag/Lgm_GradBvec2.c b/libLanlGeoMag/Lgm_GradBvec2.c new file mode 100644 index 000000000..b9428ce33 --- /dev/null +++ b/libLanlGeoMag/Lgm_GradBvec2.c @@ -0,0 +1,274 @@ +#include +#include +#include "Lgm/Lgm_MagModelInfo.h" + +/** + * \brief + * Compute the gradient of B-vec at a given point. + * + * \details + * Similar to Lgm_GradBvec(), except this routine also computes time + * derivatives of \f$ \vec{B} \f$. Computes \f$ {\nabla} \vec{B}, which + * is a tensor.\f$. To make the indices consistent with the Lgm_GradBvec() + * version, the time derivs are put in at the highest index. + * + * Each element of the m array holds a Lgm_MagModelInfo structure that is + * initialized with the value of the desired discrete time array. E.g., + * if you wanted to run for a whole day at dt = 10s, then you need an + * array that is 8640 elements, and preinitialize the Lgm_MagModelInfo + * structure, for that index with that time. The derivatives are done with + * finite differences by using the mInfo structures that range over the + * appropriate indices. E.g., if j = 362 and you choose a six-point + * scheme, then time derivs will be based on indices 362-3 -> 362+3. Some + * things to make sure of that may or may not be checked: (1) Make sure + * the j-N > 0 and j+N < n; (2) Make sure the delta-Ts are the same and + * are the same as given in the dt argument. + * + * + * + * + * \param[in] j The current time index into the array of times that is implied by the m array. + * \param[in] u0 Position (in GSM) to use. + * \param[out] GradBvec The computed gradient of Bvec at position u0, as well as the dB/dt vector + * \param[in] DerivScheme Derivative scheme to use (can be one of LGM_DERIV_SIX_POINT, LGM_DERIV_FOUR_POINT, or LGM_DERIV_TWO_POINT). + * \param[in] h The delta (in Re) to use for grid spacing in the derivative scheme. + * \param[in] n The number of elements in the m array. + * \param[in] dt The Delta-time value used in the m array. + * \param[in,out] m An array of properly initialized and configured Lgm_MagModelInfo structures. + * + * + * \author Mike Henderson + * \date 2011 + * + */ +void Lgm_GradBvec2( int j, Lgm_Vector *u0, double GradBvec[4][4], int DerivScheme, double h, int n, double dt, Lgm_MagModelInfo **m ) { + + double H, s, d, dd[7], D; + double ft[7], fx[7], fy[7], fz[7]; + double gt[7], gx[7], gy[7], gz[7]; + double qt[7], qx[7], qy[7], qz[7]; + int i, N; + double dbx_dt, dbx_dx, dbx_dy, dbx_dz; + double dby_dt, dby_dx, dby_dy, dby_dz; + double dbz_dt, dbz_dx, dbz_dy, dbz_dz; + Lgm_Vector u, Bvec; + + + /* + * Select the derivative scheme to use. + * f_0^(1) = 1/(60h) ( f_3 - 9f_2 + 45f_1 - 45f_-1 + 9f_-2 - f_-3 ) + * See page 450 of CRC standard Math tables 28th edition. + */ + switch ( DerivScheme ) { + case LGM_DERIV_SIX_POINT: + N = 3; + break; + case LGM_DERIV_FOUR_POINT: + N = 2; + break; + case LGM_DERIV_TWO_POINT: + N = 1; + break; + } + + if ( ( j-N < 0 ) || ( j+N > n-1 ) ){ + printf( "Lgm_GradBvec2: Error, requested time index not in range. j = %d, N = %d, n = %d, j-N = %d\n", j, N, n, j-N); + exit(0); + } + + for (i=-N; i<=N; ++i) dd[i] = m[j+i]->c->TT.Time; + for (i=-N; i 1e-12 ) { + printf( "Lgm_GradBvec2: Error, times are inconsistent. D, dt = %.14lf %.14lf fabs(D-dt) = %g\n", D, dt, fabs(D-dt)); + exit(0); + } + } + + + + + + /* + * Compute all first order derivatives of magnetic field components. + * I.e., using subscript notation for derivs, we need these quantities: + * bx_t, bx_x, bx_y, bx_z + * by_t, by_x, by_y, by_z + * bz_t, bz_x, bz_y, bz_z + */ + if (m[j]->VerbosityLevel > 0) printf("\t\tLgm_GradB: Computing GradB with DerivScheme = %d, h = %g", DerivScheme, h); + + /* + * Bx Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + m[j+i]->Bfield( u0, &Bvec, m[j+i] ); // variation in Time + ft[i+N] = Bvec.x; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + fx[i+N] = Bvec.x; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + fy[i+N] = Bvec.x; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + fz[i+N] = Bvec.x; + } + } + + /* + * By Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + m[j+i]->Bfield( u0, &Bvec, m[j+i] ); // Variation in TIME + gt[i+N] = Bvec.y; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + gx[i+N] = Bvec.y; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + gy[i+N] = Bvec.y; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + gz[i+N] = Bvec.y; + } + } + + + /* + * Bz Components + */ + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + m[j+i]->Bfield( u0, &Bvec, m[j+i] ); // variation in Time + qt[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.x += H; // variation in X-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + qx[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.y += H; // variation in Y-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + qy[i+N] = Bvec.z; + } + } + for (i=-N; i<=N; ++i){ + if ( N != 0 ) { + u = *u0; H = (double)i*h; u.z += H; // variation in Z-COMP + m[j]->Bfield( &u, &Bvec, m[j] ); + qz[i+N] = Bvec.z; + } + } + + + if (DerivScheme == LGM_DERIV_SIX_POINT){ + + d = 1.0/(60.0*dt); + s = 1.0/(60.0*h); + + + dbx_dt = (ft[6] - 9.0*ft[5] + 45.0*ft[4] - 45.0*ft[2] + 9.0*ft[1] - ft[0])*d; + dbx_dx = (fx[6] - 9.0*fx[5] + 45.0*fx[4] - 45.0*fx[2] + 9.0*fx[1] - fx[0])*s; + dbx_dy = (fy[6] - 9.0*fy[5] + 45.0*fy[4] - 45.0*fy[2] + 9.0*fy[1] - fy[0])*s; + dbx_dz = (fz[6] - 9.0*fz[5] + 45.0*fz[4] - 45.0*fz[2] + 9.0*fz[1] - fz[0])*s; + + dby_dt = (gt[6] - 9.0*gt[5] + 45.0*gt[4] - 45.0*gt[2] + 9.0*gt[1] - gt[0])*d; + dby_dx = (gx[6] - 9.0*gx[5] + 45.0*gx[4] - 45.0*gx[2] + 9.0*gx[1] - gx[0])*s; + dby_dy = (gy[6] - 9.0*gy[5] + 45.0*gy[4] - 45.0*gy[2] + 9.0*gy[1] - gy[0])*s; + dby_dz = (gz[6] - 9.0*gz[5] + 45.0*gz[4] - 45.0*gz[2] + 9.0*gz[1] - gz[0])*s; + + dbz_dt = (qt[6] - 9.0*qt[5] + 45.0*qt[4] - 45.0*qt[2] + 9.0*qt[1] - qt[0])*d; + dbz_dx = (qx[6] - 9.0*qx[5] + 45.0*qx[4] - 45.0*qx[2] + 9.0*qx[1] - qx[0])*s; + dbz_dy = (qy[6] - 9.0*qy[5] + 45.0*qy[4] - 45.0*qy[2] + 9.0*qy[1] - qy[0])*s; + dbz_dz = (qz[6] - 9.0*qz[5] + 45.0*qz[4] - 45.0*qz[2] + 9.0*qz[1] - qz[0])*s; + + } else if (DerivScheme == LGM_DERIV_FOUR_POINT){ + + d = 1.0/(12.0*dt); + s = 1.0/(12.0*h); + + dbx_dt = (-ft[4] + 8.0*ft[3] - 8.0*ft[1] + ft[0])*d; + dbx_dx = (-fx[4] + 8.0*fx[3] - 8.0*fx[1] + fx[0])*s; + dbx_dy = (-fy[4] + 8.0*fy[3] - 8.0*fy[1] + fy[0])*s; + dbx_dz = (-fz[4] + 8.0*fz[3] - 8.0*fz[1] + fz[0])*s; + + dby_dt = (-gt[4] + 8.0*gt[3] - 8.0*gt[1] + gt[0])*d; + dby_dx = (-gx[4] + 8.0*gx[3] - 8.0*gx[1] + gx[0])*s; + dby_dy = (-gy[4] + 8.0*gy[3] - 8.0*gy[1] + gy[0])*s; + dby_dz = (-gz[4] + 8.0*gz[3] - 8.0*gz[1] + gz[0])*s; + + dbz_dt = (-qt[4] + 8.0*qt[3] - 8.0*qt[1] + qt[0])*d; + dbz_dx = (-qx[4] + 8.0*qx[3] - 8.0*qx[1] + qx[0])*s; + dbz_dy = (-qy[4] + 8.0*qy[3] - 8.0*qy[1] + qy[0])*s; + dbz_dz = (-qz[4] + 8.0*qz[3] - 8.0*qz[1] + qz[0])*s; + + } else if (DerivScheme == LGM_DERIV_TWO_POINT){ + + d = 1.0/(2.0*dt); + s = 1.0/(2.0*h); + + dbx_dt = (ft[2] - ft[0])*d; + dbx_dx = (fx[2] - fx[0])*s; + dbx_dy = (fy[2] - fy[0])*s; + dbx_dz = (fz[2] - fz[0])*s; + + dby_dt = (gt[2] - gt[0])*d; + dby_dx = (gx[2] - gx[0])*s; + dby_dy = (gy[2] - gy[0])*s; + dby_dz = (gz[2] - gz[0])*s; + + dbz_dt = (qt[2] - qt[0])*d; + dbz_dx = (qx[2] - qx[0])*s; + dbz_dy = (qy[2] - qy[0])*s; + dbz_dz = (qz[2] - qz[0])*s; + + } + if (m[j]->VerbosityLevel > 0) { + printf(" = (%8g %8g %8g)\n", dbx_dx, dbx_dy, dbx_dz ); + printf(" Grad Bvec = (%8g %8g %8g)\n", dby_dx, dby_dy, dby_dz ); + printf(" = (%8g %8g %8g)\n", dbz_dx, dbz_dy, dbz_dz ); + } + + GradBvec[0][0] = 1.0; GradBvec[0][1] = 0.0; GradBvec[0][2] = 0.0; GradBvec[0][3] = 0.0; + GradBvec[1][0] = dbx_dt; GradBvec[1][1] = dbx_dx; GradBvec[1][2] = dbx_dy; GradBvec[1][3] = dbx_dz; + GradBvec[2][0] = dby_dt; GradBvec[2][1] = dby_dx; GradBvec[2][2] = dby_dy; GradBvec[2][3] = dby_dz; + GradBvec[3][0] = dbz_dt; GradBvec[3][1] = dbz_dx; GradBvec[3][2] = dbz_dy; GradBvec[3][3] = dbz_dz; + + + + + return; + +} diff --git a/libLanlGeoMag/Lgm_InitMagEphemInfo.c b/libLanlGeoMag/Lgm_InitMagEphemInfo.c index a9074615e..6576f02be 100644 --- a/libLanlGeoMag/Lgm_InitMagEphemInfo.c +++ b/libLanlGeoMag/Lgm_InitMagEphemInfo.c @@ -23,10 +23,10 @@ void Lgm_SetMagEphemLstarQuality( int Quality, int nFLsInDriftShell, Lgm_MagEphe MagEphemInfo->LstarQuality = 3; } - if ( (nFLsInDriftShell >= 6) && (nFLsInDriftShell <= 240) ){ + if ( (nFLsInDriftShell >= 6) && (nFLsInDriftShell < LGM_LSTARINFO_MAX_FL) ){ MagEphemInfo->nFLsInDriftShell = nFLsInDriftShell; } else { - printf( "Lgm_MagEphemSetLstarQuality: nFLsInDriftShell not in range [6,240] (Got %d). Setting to 24).\n", nFLsInDriftShell ); + printf( "Lgm_MagEphemSetLstarQuality: nFLsInDriftShell not in range [6, %d] (Got %d). Setting to 24).\n", LGM_LSTARINFO_MAX_FL, nFLsInDriftShell ); MagEphemInfo->nFLsInDriftShell = 24; } @@ -104,6 +104,7 @@ void Lgm_InitMagEphemInfoDefaults( Lgm_MagEphemInfo *MagEphemInfo, int MaxPitchA LGM_ARRAY_2D( MagEphemInfo->ShellMirror_Ps, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, Lgm_Vector ); LGM_ARRAY_2D( MagEphemInfo->ShellMirror_Ss, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, double ); LGM_ARRAY_2D( MagEphemInfo->ShellI, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, double ); + LGM_ARRAY_2D( MagEphemInfo->nBounceRegions, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, int ); LGM_ARRAY_2D( MagEphemInfo->Shell_Bmin, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, Lgm_Vector ); LGM_ARRAY_2D( MagEphemInfo->Shell_Pmin, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, Lgm_Vector ); LGM_ARRAY_2D( MagEphemInfo->Shell_GradI, MaxPitchAngles, LGM_LSTARINFO_MAX_FL, Lgm_Vector ); @@ -150,6 +151,7 @@ void Lgm_FreeMagEphemInfo_Children( Lgm_MagEphemInfo *MagEphemInfo ) { LGM_ARRAY_2D_FREE( MagEphemInfo->ShellMirror_Ps ); LGM_ARRAY_2D_FREE( MagEphemInfo->ShellMirror_Ss ); LGM_ARRAY_2D_FREE( MagEphemInfo->ShellI ); + LGM_ARRAY_2D_FREE( MagEphemInfo->nBounceRegions ); LGM_ARRAY_2D_FREE( MagEphemInfo->Shell_Bmin ); LGM_ARRAY_2D_FREE( MagEphemInfo->Shell_Pmin ); LGM_ARRAY_2D_FREE( MagEphemInfo->Shell_GradI ); @@ -207,38 +209,43 @@ void WriteMagEphemInfoStruct( char *Filename, int nPitchAngles, Lgm_MagEphemInfo dum = write( fd, MagEphemInfo->nShellPoints, nPitchAngles*sizeof( int ) ); dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Pn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); - dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Sn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Bn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Ps[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Ss[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellSphericalFootprint_Bs[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Ps[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Ss[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Bs[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Pn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Sn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellEllipsoidFootprint_Bn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->ShellMirror_Pn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); dum = write( fd, &MagEphemInfo->ShellMirror_Sn[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellMirror_Ps[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); dum = write( fd, &MagEphemInfo->ShellMirror_Ss[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); dum = write( fd, &MagEphemInfo->ShellI[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->nBounceRegions[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( int ) ); + dum = write( fd, &MagEphemInfo->Shell_Pmin[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); - dum = write( fd, &MagEphemInfo->nFieldPnts[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( int ) ); + dum = write( fd, &MagEphemInfo->nFieldPnts[0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*sizeof( int ) ); dum = write( fd, &MagEphemInfo->s_gsm[0][0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*1000*sizeof( double ) ); dum = write( fd, &MagEphemInfo->Bmag[0][0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*1000*sizeof( double ) ); dum = write( fd, &MagEphemInfo->x_gsm[0][0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*1000*sizeof( double ) ); dum = write( fd, &MagEphemInfo->y_gsm[0][0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*1000*sizeof( double ) ); dum = write( fd, &MagEphemInfo->z_gsm[0][0][0], nPitchAngles*LGM_LSTARINFO_MAX_FL*1000*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->LHilton[0], nPitchAngles*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->LMcIlwain[0], nPitchAngles*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->Hmin[0], nPitchAngles*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->Hmin_GeodLat[0], nPitchAngles*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->Hmin_GeodLon[0], nPitchAngles*sizeof( double ) ); - dum = write( fd, &MagEphemInfo->Lstar[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->LHilton[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->LMcIlwain[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->Hmin[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->Hmin_GeodLat[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->Hmin_GeodLon[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->Lstar[0], nPitchAngles*sizeof( double ) ); + dum = write( fd, &MagEphemInfo->DriftOrbitType[0], nPitchAngles*sizeof( int ) ); + close(fd); @@ -366,6 +373,14 @@ void ReadMagEphemInfoStruct( char *Filename, int *nPitchAngles, Lgm_MagEphemInfo dum = read( fd, ddata, n*LGM_LSTARINFO_MAX_FL*sizeof( double ) ); LGM_ARRAY_FROM_DATA_2D( MagEphemInfo->ShellI, ddata, n, LGM_LSTARINFO_MAX_FL, double ); + ddata = (int *)calloc( n*LGM_LSTARINFO_MAX_FL, sizeof(int) ); + dum = read( fd, ddata, n*LGM_LSTARINFO_MAX_FL*sizeof( int ) ); + LGM_ARRAY_FROM_DATA_2D( MagEphemInfo->nBounceRegions, ddata, n, LGM_LSTARINFO_MAX_FL, int ); + + vdata = (Lgm_Vector *)calloc( n*LGM_LSTARINFO_MAX_FL, sizeof(Lgm_Vector) ); + dum = read( fd, vdata, n*LGM_LSTARINFO_MAX_FL*sizeof( Lgm_Vector ) ); + LGM_ARRAY_FROM_DATA_2D( MagEphemInfo->Shell_Pmin, vdata, n, LGM_LSTARINFO_MAX_FL, Lgm_Vector ); + idata = (int *)calloc( n*LGM_LSTARINFO_MAX_FL, sizeof(int) ); @@ -420,6 +435,11 @@ void ReadMagEphemInfoStruct( char *Filename, int *nPitchAngles, Lgm_MagEphemInfo dum = read( fd, ddata, n*sizeof( double ) ); LGM_ARRAY_FROM_DATA_1D( MagEphemInfo->Lstar, ddata, n, double ); + idata = (int *)calloc( n, sizeof(int) ); + dum = read( fd, idata, n*sizeof( int ) ); + LGM_ARRAY_FROM_DATA_1D( MagEphemInfo->DriftOrbitType, idata, n, int ); + + close(fd); @@ -461,6 +481,7 @@ Lgm_MagEphemData *Lgm_InitMagEphemData( int nRows, int nPA ) { LGM_ARRAY_1D( MagEphemData->H5_TiltAngle, nRows, double ); LGM_ARRAY_1D( MagEphemData->H5_InOut, nRows, int ); LGM_ARRAY_1D( MagEphemData->H5_OrbitNumber, nRows, int ); + LGM_ARRAY_1D( MagEphemData->H5_SunAngle, nRows, double ); LGM_ARRAY_2D( MagEphemData->H5_Rgeo, nRows, 3, double ); diff --git a/libLanlGeoMag/Lgm_InitMagInfo.c b/libLanlGeoMag/Lgm_InitMagInfo.c index 03d945305..957dcbceb 100644 --- a/libLanlGeoMag/Lgm_InitMagInfo.c +++ b/libLanlGeoMag/Lgm_InitMagInfo.c @@ -84,7 +84,7 @@ void Lgm_InitMagInfoDefaults( Lgm_MagModelInfo *MagInfo ) { MagInfo->Lgm_MagStep_BS_reject = FALSE; MagInfo->Lgm_MagStep_BS_prev_reject = FALSE; MagInfo->Lgm_MagStep_BS_atol = 1e-5; - MagInfo->Lgm_MagStep_BS_rtol = 1e-5; + MagInfo->Lgm_MagStep_BS_rtol = 0.0; /* * Some inits for MagStep_RK5 @@ -124,6 +124,11 @@ void Lgm_InitMagInfoDefaults( Lgm_MagModelInfo *MagInfo ) { MagInfo->Lgm_TraceToBmin_Tol = 1e-7; MagInfo->Lgm_TraceLine_Tol = 1e-7; + MagInfo->Lgm_n_V_integrand_Calls = 0; + MagInfo->Lgm_V_Integrator_epsrel = 0.0; + MagInfo->Lgm_V_Integrator_epsabs = 1e-3; + MagInfo->Lgm_V_Integrator = DQAGS; + /* @@ -188,6 +193,16 @@ void Lgm_InitMagInfoDefaults( Lgm_MagModelInfo *MagInfo ) { MagInfo->RBF_Type = LGM_RBF_MULTIQUADRIC; MagInfo->RBF_Eps = 1.0/(4.0*4.0); + MagInfo->KdTree = NULL; + MagInfo->KdTree_Alloced = FALSE; + MagInfo->KdTree_kNN_InterpMethod = 0; + MagInfo->KdTree_kNN_k = 12; + MagInfo->KdTree_kNN_MaxDist2 = 1e6; + + MagInfo->KdTree_kNN = NULL; + MagInfo->KdTree_kNN_Alloced = FALSE; + + MagInfo->KdTreeCopy = FALSE; /* @@ -206,6 +221,20 @@ void Lgm_FreeMagInfo_children( Lgm_MagModelInfo *Info ) { Lgm_DeAllocate_TS07( &(Info->TS07_Info) ); Lgm_free_ctrans( Info->c ); + if ( Info->KdTree_Alloced ) { + + if ( Info->KdTreeCopy ) { + Lgm_KdTree_FreeLite( Info->KdTree ); + } else { + Lgm_KdTree_Free( Info->KdTree ); + } + Info->KdTree_Alloced = FALSE; + + } + + if ( Info->KdTree_kNN_Alloced ) { + LGM_ARRAY_1D_FREE( Info->KdTree_kNN ); + } // Lgm_FreeFastPow( Info->f ); @@ -264,7 +293,6 @@ Lgm_MagModelInfo *Lgm_CopyMagInfo( Lgm_MagModelInfo *s ) { //t->spline = (gsl_spline *)NULL; // octree stuff is also not copied correctly... - if ( s->Octree_Alloced ) { //t->Octree = Lgm_CopyOctree( s->Octree ); t->Octree = s->Octree; @@ -275,6 +303,31 @@ Lgm_MagModelInfo *Lgm_CopyMagInfo( Lgm_MagModelInfo *s ) { } + + /* Do a "CopyLite" on the KdTree if its alloced -- this make a new independent copy with the exception of the tree data. The tree data is + * not copied, we just copy the pointer. To guard against freein this, we flag that this is a copy. + */ +//printf("s->KdTree_Alloced = %d\n", s->KdTree_Alloced); +//printf("s->KdTree = %s\n", s->KdTree); + if ( s->KdTree_Alloced ) { + t->KdTree = Lgm_KdTree_CopyLite( s->KdTree ); + t->KdTree_Alloced = TRUE; + t->KdTreeCopy = TRUE; + } else { + t->KdTree = NULL; + t->KdTree_Alloced = FALSE; + t->KdTreeCopy = FALSE; + } + + // dont copy the pointerj here. + t->KdTree_kNN = NULL; + t->KdTree_kNN_Alloced = 0; + + // Make sure the RBF hash tables are reset. + t->rbf_ht_alloced = FALSE; + + + /* * Copy the TS07_Info structure */ @@ -528,6 +581,15 @@ m->Lgm_MagStep_Integrator = LGM_MAGSTEP_ODE_BS; strcpy( m->ExtMagModelStr3, "Reference: Uses KDTree and nearest neighbor algorithm to interpolate from unstructured data clouds."); strcpy( m->ExtMagModelStr4, "Comments: Any 3D collection of B-field data points can be used." ); break; + case LGM_EXTMODEL_SCATTERED_DATA6: + m->Bfield = Lgm_B_FromScatteredData6; + m->Lgm_MagStep_Integrator = LGM_MAGSTEP_ODE_RK5; +m->Lgm_MagStep_Integrator = LGM_MAGSTEP_ODE_BS; + strcpy( m->ExtMagModelStr1, "ScatteredData6" ); + strcpy( m->ExtMagModelStr2, "ScatteredData6" ); + strcpy( m->ExtMagModelStr3, "Reference: Uses KDTree and nearest neighbor algorithm to interpolate from unstructured data clouds."); + strcpy( m->ExtMagModelStr4, "Comments: Any 3D collection of B-field data points can be used." ); + break; default: @@ -679,3 +741,69 @@ void Lgm_Set_Lgm_B_IGRF_InternalModel(Lgm_MagModelInfo *MagInfo) { MagInfo->InternalModel = LGM_IGRF; } +/* + * Set transformation matrix to go from GSM to PQB coords. + * + * B: z-like coordinate pointing in direction of local b. + * Q: y-like coordinate that is perp to both radial direction and b-hat + * P: x-like coordinate completes right handed system. + */ +void Lgm_Set_GSM_TO_PQB( Lgm_Vector *Position, Lgm_MagModelInfo *m ){ + + Lgm_Vector P, Q, B, R; + + + /* + * Get B in GSM coords. + */ + m->Bfield( Position, &B, m ); Lgm_NormalizeVector( &B ); // B in GSM coords + + /* + * The radial direction is just -Position + */ + R.x = -Position->x; R.y = -Position->y; R.z = -Position->z; + + /* + * Compute Q = R x B + */ + Lgm_CrossProduct( &R, &B, &Q ); Lgm_NormalizeVector( &Q ); // Q in GSM coords + + /* + * Compute P = B x Q + */ + Lgm_CrossProduct( &Q, &B, &P ); Lgm_NormalizeVector( &P ); // P in GSM coords + + m->Agsm_to_pqb[0][0] = P.x, m->Agsm_to_pqb[1][0] = P.y, m->Agsm_to_pqb[2][0] = P.z; + m->Agsm_to_pqb[0][1] = Q.x, m->Agsm_to_pqb[1][1] = Q.y, m->Agsm_to_pqb[2][1] = Q.z; + m->Agsm_to_pqb[0][2] = B.x, m->Agsm_to_pqb[1][2] = B.y, m->Agsm_to_pqb[2][2] = B.z; + + m->Apqb_to_gsm[0][0] = P.x, m->Apqb_to_gsm[1][0] = Q.x, m->Apqb_to_gsm[2][0] = B.x; + m->Apqb_to_gsm[0][1] = P.y, m->Apqb_to_gsm[1][1] = Q.y, m->Apqb_to_gsm[2][1] = B.y; + m->Apqb_to_gsm[0][2] = P.z, m->Apqb_to_gsm[1][2] = Q.z, m->Apqb_to_gsm[2][2] = B.z; + + m->Agsm_to_pqb_set = TRUE; + +} + +void Lgm_GSM_TO_PQB( Lgm_Vector *u_gsm, Lgm_Vector *u_pqb, Lgm_MagModelInfo *m ){ + + if ( m->Agsm_to_pqb_set == FALSE ){ + printf( "You need to set the transformations matrices first using Lgm_Set_GSM_TO_PQB()\n"); + } else { + Lgm_MatTimesVec( m->Agsm_to_pqb, u_gsm, u_pqb ); + } + +} + + +void Lgm_PQB_TO_GSM( Lgm_Vector *u_pqb, Lgm_Vector *u_gsm, Lgm_MagModelInfo *m ){ + + if ( m->Agsm_to_pqb_set == FALSE ){ + printf( "You need to set the transformations matrices first using Lgm_Set_GSM_TO_PQB()\n"); + } else { + Lgm_MatTimesVec( m->Apqb_to_gsm, u_gsm, u_pqb ); + } + +} + + diff --git a/libLanlGeoMag/Lgm_KdTree.c b/libLanlGeoMag/Lgm_KdTree.c index 4deea71e1..4b91dae87 100644 --- a/libLanlGeoMag/Lgm_KdTree.c +++ b/libLanlGeoMag/Lgm_KdTree.c @@ -17,6 +17,8 @@ #include #include +// We are missing the Lgm_KdTree_Free() routine??? + /** @@ -27,18 +29,20 @@ * Given arrays of positions and data, this routine recursively partitions * the data into a kdtree data structure. * - * \param[in] Points An array of position vectors in D-dimensional space. ObjectPoints[d][n] is the dth component of the nth point. + * \param[in] Points An array of position vectors in D-dimensional + * space. ObjectPoints[d][n] is the dth component of the nth point. * - * \param[in] Objects An array of objects in D-dimensional space. Objects[n] is the nth pointer to an object. - * This is an array of 'void *' pointers. This allows the user to use any object type here, so long as they are properly - * typecast. + * \param[in] Objects An array of objects in D-dimensional space. + * Objects[n] is the nth pointer to an object. This is an array of 'void *' + * pointers. This allows the user to use any object type here, so long as + * they are properly typecast. * * \param[in] N Number of points. * * \param[in] D Number of dimensions. * * \returns returns a pointer to the a KdTree structure. User is - * responsible to freeing this with Lgm_FreeKdTree( ) + * responsible to freeing this with Lgm_KdTree_Free( ) * * \author Mike Henderson * \date 2013 @@ -102,6 +106,80 @@ Lgm_KdTree *Lgm_KdTree_Init( double **Positions, void **Objects, unsigned long i } +/* + * Frees a Lgm_KdTree +NOT FINISHED!!!!! +NEED TO TRAVERSE TREE AND DEALLOCATE EVERYTHING. + */ +void Lgm_KdTree_Free( Lgm_KdTree *kt ) { + + + if ( kt == NULL ) return; + if ( kt->Root != NULL ) { + Lgm_FreeKdTreeNode( kt->Root ); + } + + Lgm_pQueue_Destroy( kt->PQN ); + Lgm_pQueue_Destroy( kt->PQP ); + + free( kt ); + +} + + + + +/* + * Copy a KdTree structure. + * + * This is NOT a full copy. Here we do not copy the actual tree. Instead, we + * copy the other items like PQN, PQP, etc... The idea is that we want to be + * able to use a tree for lookups in parallel threads. In this mode, the tree + * and the data in the tree are not modified. + * + */ +Lgm_KdTree *Lgm_KdTree_CopyLite( Lgm_KdTree *ks ) { + + Lgm_KdTree *kt; + + // alloc mem for target + kt = (Lgm_KdTree *) calloc( 1, sizeof( Lgm_KdTree) ); + + kt->kNN_Lookups = 0; + kt->SplitStrategy = ks->SplitStrategy; + + /* copy pointer to the actual root nood of tree. + * Do not free this. + */ + kt->Root = ks->Root; + + /* + * When done, these will need to be properly free'd, without freeing the tree + * (the master copy should only free it once.) + */ + kt->PQN = Lgm_pQueue_Create( 5000 ); + kt->PQP = Lgm_pQueue_Create( 5000 ); + + return( kt ); + + +} + +/* + * Frees just the extra bits alloced by Lgm_KdTree_CopyLite(). Leaves initial + * tree unfreed. + */ +void Lgm_KdTree_FreeLite( Lgm_KdTree *kt ) { + + if ( kt == NULL ) return; + + if ( kt->PQN != NULL ) Lgm_pQueue_Destroy( kt->PQN ); + if ( kt->PQP != NULL ) Lgm_pQueue_Destroy( kt->PQP ); + + free( kt ); + +} + /** @@ -115,7 +193,7 @@ Lgm_KdTree *Lgm_KdTree_Init( double **Positions, void **Objects, unsigned long i * * \param[in] D Integer dimension of the KdTree. * - * \returns void + * \returns pointer to Lgm_KdTreeNode * * \author Mike Henderson * \date 2013 @@ -151,6 +229,60 @@ Lgm_KdTreeNode *Lgm_CreateKdTreeRoot( int D ) { } +/** + * \brief + * Recursively free the binary KdTree created int Lgm_KdTree_Init() + * + * \details + * Recursive de-allocation of all memory used. + * + * \param[in] *Node Pointer to Root Node. + * + * \returns void + * + * \author Mike Henderson + * \date 2017 + * + */ +void Lgm_FreeKdTreeNode( Lgm_KdTreeNode *Node ) { + + int j; + + if ( Node == NULL ) return; + + if ( Node->nData > 0 ) { + + /* + * Its a leaf node that stores data -- free it. (Should not have Left + * or Right set). + */ + if ( Node->Data != NULL ) { + for (j=0; jnData; j++) { + if ( Node->Data[j].Position != NULL ) { + free( Node->Data[j].Position ); + } + } + free( Node->Data ); + } + + } else { + + if ( Node->Left != NULL ) Lgm_FreeKdTreeNode( Node->Left ); + if ( Node->Right != NULL ) Lgm_FreeKdTreeNode( Node->Right ); + + } + + if ( Node->Min != NULL ) free( Node->Min ); + if ( Node->Max != NULL ) free( Node->Max ); + if ( Node->Diff != NULL ) free( Node->Diff ); + free( Node ); + + return; + +} + + + /** * \brief @@ -368,6 +500,9 @@ void Lgm_KdTree_SubDivideVolume( Lgm_KdTreeNode *t, Lgm_KdTree *kt ) { */ for (j=0; jnData; j++) free( t->Data[j].Position ); free( t->Data ); t->Data = NULL; +//free( t->Min ); +//free( t->Max ); +//free( t->Diff ); t->nDataBelow = t->nData; t->nData = 0; @@ -449,7 +584,8 @@ int Lgm_KdTree_kNN( double *q, int D, Lgm_KdTree *KdTree, int K, int *Kgot, doub } //Lgm_KdTree_PrintPQ( &PQ ); //only for debugging - ++(KdTree->kNN_Lookups); +// not thread safe ? +// ++(KdTree->kNN_Lookups); /* * return success @@ -969,6 +1105,7 @@ int Lgm_KdTree_kNN2( double *q_in, int D, Lgm_KdTree *KdTree, int K, int *Kgot, //Lgm_KdTree_PrintPQ( &PQ ); //only for debugging +// Not thread safe? ++(KdTree->kNN_Lookups); @@ -978,10 +1115,9 @@ int Lgm_KdTree_kNN2( double *q_in, int D, Lgm_KdTree *KdTree, int K, int *Kgot, free( q ); if (k==K) { return( KDTREE_KNN_SUCCESS ); - } - else { + } else { return( KDTREE_KNN_TOO_FEW_NNS ); - } + } } #pragma GCC pop_options diff --git a/libLanlGeoMag/Lgm_MagEphemWriteHdf.c b/libLanlGeoMag/Lgm_MagEphemWriteHdf.c index b90b4eae2..9ed2403d1 100644 --- a/libLanlGeoMag/Lgm_MagEphemWriteHdf.c +++ b/libLanlGeoMag/Lgm_MagEphemWriteHdf.c @@ -215,6 +215,30 @@ void Lgm_WriteMagEphemHeaderHdf( hid_t file, char *CodeVersion, char *ExtModel, status = H5Dclose( DataSet ); + // Create SunAngle Dataset + DataSet = CreateExtendableRank1DataSet( file, "SunAngle", H5T_NATIVE_DOUBLE, &space ); + Lgm_WriteStringAttr( DataSet, "DESCRIPTION", "SunAngle" ); + Lgm_WriteStringAttr( DataSet, "DEPEND_0", "IsoTime" ); + Lgm_WriteStringAttr( DataSet, "UNITS", "degrees" ); + Lgm_WriteStringAttr( DataSet, "SCALETYP", "linear" ); + Lgm_WriteStringAttr( DataSet, "FILLVAL", "-1E31" ); + Lgm_WriteStringAttr( DataSet, "VAR_TYPE", "data" ); + status = H5Sclose( space ); + status = H5Dclose( DataSet ); + + + // Create Eclipse Flag Dataset + DataSet = CreateExtendableRank1DataSet( file, "Eclipse", H5T_NATIVE_INT, &space ); + Lgm_WriteStringAttr( DataSet, "DESCRIPTION", "Eclipse Flag" ); + Lgm_WriteStringAttr( DataSet, "DEPEND_0", "IsoTime" ); + Lgm_WriteStringAttr( DataSet, "UNITS", "dimless" ); + Lgm_WriteStringAttr( DataSet, "SCALETYP", "linear" ); + Lgm_WriteStringAttr( DataSet, "FILLVAL", "-1E31" ); + Lgm_WriteStringAttr( DataSet, "VAR_TYPE", "data" ); + status = H5Sclose( space ); + status = H5Dclose( DataSet ); + + // Create Rgeo Dataset DataSet = CreateExtendableRank2DataSet( file, "Rgeo", 3, H5T_NATIVE_DOUBLE, &space ); Lgm_WriteStringAttr( DataSet, "DESCRIPTION", "Geocentric Geographic position vector of S/C." ); @@ -1126,6 +1150,30 @@ void Lgm_WriteMagEphemDataHdf( hid_t file, int iRow, int i, Lgm_MagEphemData *me LGM_HDF5_EXTEND_RANK1_DATASET( file, "DipoleTiltAngle", iRow, H5T_NATIVE_DOUBLE, &med->H5_TiltAngle[i] ); // Write DipoleTiltAngle LGM_HDF5_EXTEND_RANK1_DATASET( file, "InOut", iRow, H5T_NATIVE_INT, &med->H5_InOut[i] ); // Write InOut LGM_HDF5_EXTEND_RANK1_DATASET( file, "OrbitNumber", iRow, H5T_NATIVE_INT, &med->H5_OrbitNumber[i] ); // Write OrbitNumber + LGM_HDF5_EXTEND_RANK1_DATASET( file, "SunAngle", iRow, H5T_NATIVE_DOUBLE, &med->H5_SunAngle[i] ); // Write SunAngle + + /* + * Kludge.... + * Add in Eclipse Flag + */ + Lgm_Vector u_gei, u_mod; + Lgm_CTrans *c = Lgm_init_ctrans(0); +c->ephModel = LGM_EPH_DE; +c->ephModel = c->ephModel = LGM_EPH_LOW_ACCURACY; + int Eclipse_Flag; + Lgm_Set_Coord_Transforms( med->H5_Date[i], med->H5_UTC[i], c ); + u_gei.x = med->H5_Rgei[i][0]; u_gei.y = med->H5_Rgei[i][1]; u_gei.z = med->H5_Rgei[i][2]; + Lgm_Convert_Coords( &u_gei, &u_mod, GEI2000_TO_MOD, c ); + Eclipse_Flag = Lgm_EarthEclipse( &u_mod, c ); +printf("med->H5_Date[i], med->H5_UTC[i] = %8ld %g\n", med->H5_Date[i], med->H5_UTC[i] ); +printf("u_gei = %g %g %g\n", u_gei.x, u_gei.y, u_gei.z); +printf("u_mod = %g %g %g\n", u_mod.x, u_mod.y, u_mod.z); +printf("Eclipse_Flag = %d\n", Eclipse_Flag ); +//double radius = Lgm_Magnitude( &u_gei ); +//if ( (radius > 2.0)&&(Eclipse_Flag>0) ) exit(0); + Lgm_free_ctrans( c ); + LGM_HDF5_EXTEND_RANK1_DATASET( file, "Eclipse", iRow, H5T_NATIVE_INT, &Eclipse_Flag ); // Write Eclipse_Flag + LGM_HDF5_EXTEND_RANK2_DATASET( file, "Rgsm", iRow, 3, H5T_NATIVE_DOUBLE, &med->H5_Rgsm[i][0] ); // Write Rgsm LGM_HDF5_EXTEND_RANK2_DATASET( file, "Rgeo", iRow, 3, H5T_NATIVE_DOUBLE, &med->H5_Rgeo[i][0] ); // Write Rgeo LGM_HDF5_EXTEND_RANK2_DATASET( file, "Rsm", iRow, 3, H5T_NATIVE_DOUBLE, &med->H5_Rsm[i][0] ); // Write Rsm @@ -1200,5 +1248,9 @@ void Lgm_WriteMagEphemDataHdf( hid_t file, int iRow, int i, Lgm_MagEphemData *me LGM_HDF5_EXTEND_RANK2_DATASET( file, "K", iRow, med->H5_nAlpha, H5T_NATIVE_DOUBLE, &med->H5_K[i][0] ); // Write K + + + + } diff --git a/libLanlGeoMag/Lgm_MaxwellJuttner.c b/libLanlGeoMag/Lgm_MaxwellJuttner.c index 9de3e64a7..14b371698 100644 --- a/libLanlGeoMag/Lgm_MaxwellJuttner.c +++ b/libLanlGeoMag/Lgm_MaxwellJuttner.c @@ -43,19 +43,47 @@ * or * * c^3 n - * f = ------------------------- exp( -(Ek + E0)/(a E0) ) (3) + * f = ------------------------- exp( -(Ek + E0)/(a E0) ) (3) * 4pi E0^3 a K2(1/a) * * - * + * * Note that a is dimensionless. If m0 c^2 = E0 is in MeV, then kT is in Mev. * The boltsmann constant k is in MeV/K, so T is in Kelvin. But we want the * temperature to be in MeV to start with, so we will replace kT by T. I.e., * * a = T/E0 + * * We will also want to end up with units of c^3 cm^-3 MeV^-3, so we will not explicity * multiply the c^3. * + * Alternate form: Let b = 1/a (=E0/T). And since E = Ek + E0, + * + * c^3 b n + * f = ------------------------- exp( -b E / E0 ) (4) + * 4pi E0^3 K2(b) + * + * For low temperature plasmas, K2(b) ~ sqrt( pi/(2 b) ) exp( -b ) + * So for b >> 1, (4) becomes: + * + * + * c^3 b n exp( b ) + * f = ------------------------- exp( -b E / E0 ) (5) + * 4pi E0^3 sqrt( pi/(2 b) ) + * + * Putting b=E0/T and E0=mc^2 then gives; + * + * n + * f = ------------------------- exp( -Ek / T ) (6) + * ( 2pi m T )^(3/2) + * + * which is just the non-relativistic maxwellian. Or in slightly different form; + * + * n c^3 + * f = ------------------------- exp( -Ek / T ) (7) + * ( 2pi E0 T )^(3/2) + * + * * * A more rigorous form of Max-Jut is given by (Chacon-Acosta, G., L. Dagdug, * H. A. Morales-Tecotl, "Manifestly covariant Juttner distribution and @@ -94,22 +122,25 @@ double Lgm_MaxJut( double n, double T, double Ek, double E0 ) { double p2c2, Theta, K2, E02, E03, f0, f; - p2c2 = Lgm_p2c2( Ek, E0 ); + //p2c2 = Lgm_p2c2( Ek, E0 ); E02 = E0*E0; // MeV^2 E03 = E02*E0; // MeV^3 - Theta = 1000.0*E0/T; // dimensionless (MeV/MeV) - if ( Theta < 100.0 ) { + T /= 1000.0; // convert T from keV -> MeV + Theta = E0/T; // dimensionless (MeV/MeV) + + if ( Theta > 10.0 ) { + // Low temperature limit. K2 ~ sqrt( pi/(2*Theta) ) exp(-Theta) + K2 = sqrt( 0.5*M_PI/Theta ); + f0 = n*Theta/( 4.0*M_PI * E03 * K2); // units of c^3 cm^-3 MeV^-3 + //f = f0*exp( Theta*(1.0-(Ek+E0)/E0) ); // units of c^3 cm^-3 MeV^-3 + f = f0*exp( -Ek/T ); // units of c^3 cm^-3 MeV^-3 + } else { // compute Bessel function K2 = gsl_sf_bessel_Kn( 2, Theta ); // dimensionless f0 = n*Theta/( 4.0*M_PI * E03 * K2); // units of c^3 cm^-3 MeV^-3 f = f0*exp( -Theta*(Ek+E0)/E0 ); // units of c^3 cm^-3 MeV^-3 - } else { - // Low temperature limit. K2 ~ sqrt( pi/(2*Theta) ) exp(-Theta) - K2 = sqrt( 0.5*M_PI/Theta ); - f0 = n*Theta/( 4.0*M_PI * E03 * K2); // units of c^3 cm^-3 MeV^-3 - f = f0*exp( Theta*(1.0-(Ek+E0)/E0) ); // units of c^3 cm^-3 MeV^-3 } @@ -117,6 +148,48 @@ double Lgm_MaxJut( double n, double T, double Ek, double E0 ) { } +/* + * Derivatives wrt n and T + */ +void Lgm_MaxJut_Derivs( double n, double T, double Ek, double E0, double *dfdn, double *dfdT ) { + double K2, E02, E03, f0, f; + double sq, ex, b, g, h, q, gnq, dK2db, dfdb, dbdT; + + + E02 = E0*E0; // MeV^2 + E03 = E02*E0; // MeV^3 + b = 1000.0*E0/T; // dimensionless (MeV/MeV) + + if ( b < 10.0 ) { + K2 = gsl_sf_bessel_Kn( 2, b ); // dimensionless + dK2db = -0.5*( gsl_sf_bessel_Kn( 1, b ) + gsl_sf_bessel_Kn( 3, b ) ); + //printf("b < 100: K2, dK2db = %g %g\n", K2, dK2db); + } else { + sq = sqrt( 0.5*M_PI/b ); + ex = exp( -b ); + K2 = sq * ex; + dK2db = -ex * sq * ( 1.0 + 0.5/b ); + //printf("b >= 100: b = %g K2, dK2db = %g %g\n", b, K2, dK2db); + } + + g = 1.0/( 4.0*M_PI * E03 ); // units of c^3 cm^-3 MeV^-3 + h = -(Ek+E0)/E0; + q = exp( b*h ); + f = n*g*b/K2 * q; // units of c^3 cm^-3 MeV^-3 + //printf("g, h, q, f = %g %g %g %g\n", g, h, q, f); + + // df/dn is trivial + *dfdn = f/n; + + // df/dT is more involved + gnq = g*n*q; + dfdb = gnq/K2 + gnq*h*b/K2 - gnq*b/(K2*K2) * dK2db; + dbdT = -1000.0*E0/(T*T); + //printf("gnq, dfdb, dbdT = %g %g %g\n", gnq, dfdb, dbdT); + *dfdT = dfdb*dbdT; + +} + /* * Non-relativistic maxwellian; @@ -131,6 +204,7 @@ double Lgm_MaxJut( double n, double T, double Ek, double E0 ) { * n c^3 * f = ------------------------- exp( -Ek/(kT) ) * (2pi E0 k T )^(3/2) + * * And kT -> T if T is in units of MeV already. * * Inputs: @@ -154,3 +228,35 @@ double Lgm_Maxwellian( double n, double T, double Ek, double E0 ) { } + +double Lgm_Maxwellian_dfdn( double n, double T, double Ek, double E0 ) { + + double dfdn; + + T /= 1000.0; // keV -> MeV + + dfdn = pow( 1.0/(2.0*M_PI*E0*T), 1.5) * exp( -Ek/T ); // units of c^3 / (cm^3 MeV^3) + + return(dfdn); + +} + + +double Lgm_Maxwellian_dfdT( double n, double T, double Ek, double E0 ) { + + double dfdT; + + T /= 1000.0; // keV -> MeV + + dfdT = n*pow( 1.0/(2.0*M_PI*E0*T), 1.5) * exp( -Ek/T ) * (Ek/(T*T) - 1.5/T); // units of c^3 / (cm^3 MeV^3) + + return(dfdT); + +} + + + + + + + diff --git a/libLanlGeoMag/Lgm_Misc.c b/libLanlGeoMag/Lgm_Misc.c index 417651d33..8e29e5184 100644 --- a/libLanlGeoMag/Lgm_Misc.c +++ b/libLanlGeoMag/Lgm_Misc.c @@ -1,47 +1,242 @@ #include "Lgm/Lgm_Misc.h" -void Lgm_ReplaceSubString( char *NewStr, char *OrigStr, char *SubStr, char *RepStr ) { +/* + * Routine to replace a substring with another string (of potentially + * different length) User provides OrigStr, SubStr, RepStr and these dont get + * modified in any way. + * + * NewStr is allocated and returned. + * User is responsible for freeing the memory. + * + */ +void Lgm_ReplaceSubString2( char **OutStr, char *OrigStr, char *SubStr, char *RepStr ) { + + int nOrigStr, nStr, nSubStr, nRepStr, n, nNewStr, nCopy1, nCopy2, done; + char *p, *Str, *Str2, *p_remaining, *NewStr; + int q; + + + // get sizes of strings + nSubStr = strlen( SubStr ); + nRepStr = strlen( RepStr ); - int nOrigStr, nStr, nSubStr, nRepStr, n, nNewStr, done; - char *p, *Str; // Make a copy of the original string nStr = strlen( OrigStr ); Str = (char *)calloc( nStr+1, sizeof(char) ); strcpy( Str, OrigStr ); - strcpy( NewStr, OrigStr ); - if ( strcmp( RepStr, SubStr ) == 0) done = 1; - else done = 0; + printf("Str = %s n = %d\n", Str, nStr); + + + // Allocate space for the NewStr -- initially we only need enough space for the null terminator + q = 1; + NewStr = (char *)calloc( q, sizeof(char) ); + NewStr[q-1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); + + + /* + * Set pointer to look at the part of Str the we have left to + * examine/replace initially it is just the start + */ + p_remaining = Str; + done = 0; while ( !done ) { - if ( !(p = strstr( Str, SubStr )) ) { + /* + * Using the string pointed to by p_remaining, attempt to locate an + * occurrence of SubStr + */ + if ( !(p = strstr( p_remaining, SubStr )) ) { + + /* + * We didn't find an occurrence of SubStr in the String pointed to + * by p_remaining. So we are done. + * But we do need to append p_remaining onto NewStr.... + */ + nCopy1 = strlen( p_remaining ); + q += nCopy1; + NewStr = (char *)realloc( NewStr, q*sizeof(char) ); + + // Append characters from start of p_remaining NewStr + strncpy( &NewStr[nNewStr], p_remaining, nCopy1 ); // this copies over everything up to and including the char before where p is pointing. + NewStr[nNewStr+nCopy1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); done = 1; } else { - // get sizes of strings - nStr = strlen( Str ); - nSubStr = strlen( SubStr ); - nRepStr = strlen( RepStr ); + /* + * We have found an occurrence of the substring. The pointer p + * is pointing at it in the string p_remaining + * + * We need to: + * + * 1) copy into the NewStr, everything in the p_remaining + * from its start up until the char just before this point + * + * 2) copy into the NewStr, the new RepStr as a replacement + * + * 3) find the place in p_remaining that follows the SubStr + * we just replaced. + * + */ + //determine how many chars we need to copy over + nCopy1 = p-p_remaining; + nCopy2 = nRepStr; + + q += nCopy1; + q += nCopy2; + + // realloc the size of the NewStr array to accomodate the part we need to copy + the size of the RepStr that will get copied + NewStr = (char *)realloc( NewStr, q*sizeof(char) ); + + // Append characters from start of p_remaining NewStr + strncpy( &NewStr[nNewStr], p_remaining, nCopy1 ); // this copies over everything up to and including the char before where p is pointing. + NewStr[nNewStr+nCopy1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); + + // Append characters from RepStr + strncpy( &NewStr[nNewStr], RepStr, nCopy2 ); // this copies over contents of the Replacement string + NewStr[nNewStr+nCopy2] = '\0'; // terminate it. + nNewStr = strlen(NewStr); + + p_remaining = p + nSubStr; + + } + + } + + free( Str ); + + + *OutStr = NewStr; + + return; + +} + + + +/* + * Legacy version... For backwards compatibility. + * Routine to replace a substring with another string (of potentially + * different length) User provides OrigStr, SubStr, RepStr and these dont get + * modified in any way. + * + * In this version, the new string is allocated by the user outside the routine. + * It must be big enough to hold the replaced substrings, so is not as safe as the other version. + */ +void Lgm_ReplaceSubString( char *OutStr, char *OrigStr, char *SubStr, char *RepStr ) { + int nOrigStr, nStr, nSubStr, nRepStr, n, nNewStr, nCopy1, nCopy2, done; + char *p, *Str, *Str2, *p_remaining, *NewStr; + int q; - // Copy characters from start of Str start to start of Orig - strncpy( NewStr, Str, p-Str ); - NewStr[p-Str] = '\0'; - sprintf( NewStr + (p-Str), "%s%s", RepStr, p + nSubStr ); + + // get sizes of strings + nSubStr = strlen( SubStr ); + nRepStr = strlen( RepStr ); + + + + // Make a copy of the original string + nStr = strlen( OrigStr ); + Str = (char *)calloc( nStr+1, sizeof(char) ); + strcpy( Str, OrigStr ); + printf("Str = %s n = %d\n", Str, nStr); + + + // Allocate space for the NewStr -- initially we only need enough space for the null terminator + q = 1; + NewStr = (char *)calloc( q, sizeof(char) ); + NewStr[q-1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); + + + + /* + * Set pointer to look at the part of Str the we have left to + * examine/replace initially it is just the start + */ + p_remaining = Str; + done = 0; + while ( !done ) { + + /* + * Using the string pointed to by p_remaining, attempt to locate an + * occurrence of SubStr + */ + if ( !(p = strstr( p_remaining, SubStr )) ) { + + /* + * We didn't find an occurrence of SubStr in the String pointed to + * by p_remaining. So we are done. + * But we do need to append p_remaining onto NewStr.... + */ + nCopy1 = strlen( p_remaining ); + q += nCopy1; + NewStr = (char *)realloc( NewStr, q*sizeof(char) ); + + // Append characters from start of p_remaining NewStr + strncpy( &NewStr[nNewStr], p_remaining, nCopy1 ); // this copies over everything up to and including the char before where p is pointing. + NewStr[nNewStr+nCopy1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); + + done = 1; + + } else { + + /* + * We have found an occurrence of the substring. The pointer p + * is pointing at it in the string p_remaining + * + * We need to: + * + * 1) copy into the NewStr, everything in the p_remaining + * from its start up until the char just before this point + * + * 2) copy into the NewStr, the new RepStr as a replacement + * + * 3) find the place in p_remaining that follows the SubStr + * we just replaced. + * + */ + //determine how many chars we need to copy over + nCopy1 = p-p_remaining; + nCopy2 = nRepStr; + + q += nCopy1; + q += nCopy2; + + // realloc the size of the NewStr array to accomodate the part we need to copy + the size of the RepStr that will get copied + NewStr = (char *)realloc( NewStr, q*sizeof(char) ); + + // Append characters from start of p_remaining NewStr + strncpy( &NewStr[nNewStr], p_remaining, nCopy1 ); // this copies over everything up to and including the char before where p is pointing. + NewStr[nNewStr+nCopy1] = '\0'; // terminate it. + nNewStr = strlen( NewStr ); + + // Append characters from RepStr + strncpy( &NewStr[nNewStr], RepStr, nCopy2 ); // this copies over contents of the Replacement string + NewStr[nNewStr+nCopy2] = '\0'; // terminate it. nNewStr = strlen(NewStr); - Str = realloc( Str, (nNewStr+1)*sizeof(char) ); - strcpy( Str, NewStr ); + p_remaining = p + nSubStr; + } } free( Str ); + + strcpy( OutStr, NewStr ); + free( NewStr); + return; } diff --git a/libLanlGeoMag/Lgm_NrlMsise00.c b/libLanlGeoMag/Lgm_NrlMsise00.c index 231ba52ad..31f0c7302 100644 --- a/libLanlGeoMag/Lgm_NrlMsise00.c +++ b/libLanlGeoMag/Lgm_NrlMsise00.c @@ -2,7 +2,7 @@ static const double RGAS = 831.4; -#define ZETA(ZZ,ZL,p) ( (ZZ-ZL)*(p->RE+ZL)/(p->RE+ZZ) ) +#define ZETA(ZZ,ZL,p) ( (ZZ-ZL)*(p->Rref+ZL)/(p->Rref+ZZ) ) /*---------------------------------------------------------------------- * SUBROUTINE GTD7(IYD,SEC,ALT,GLAT,GLONG,STL,F107A,F107,AP,MASS,D,T) @@ -174,7 +174,7 @@ V1 =1; */ XLAT = GLAT; if ( p->SW[2] == 0 ) XLAT = 45.0; - GLATF( XLAT, &p->GSURF, &p->RE ); + GLATF( XLAT, &p->GSURF, &p->Rref ); XMM = p->PDM[5][3]; @@ -516,7 +516,7 @@ void GHP7( int IYD, double SEC, double ALT, double GLAT, double GLONG, double ST if ( (fabs(DIFF) < TEST) || (L == LTEST) ) break; XM = D[6]/(XN*1.66E-24); if ( p->IMR == 1 ) XM *= 1.0e3; - g = 1.0 + Z/p->RE; g2 = g*g; + g = 1.0 + Z/p->Rref; g2 = g*g; G = p->GSURF/g2; SH = RGAS*T[2]/(XM*G); // New altitude estimate using scale height @@ -1224,7 +1224,7 @@ void METERS( int METER, Lgm_Msis00Info *p ) { double SCALH( double ALT, double XM, double TEMP, Lgm_Msis00Info *p ) { double q, q2, G; // static double RGAS = 831.4; - q = 1.0+ALT/p->RE; q2 = q*q; + q = 1.0+ALT/p->Rref; q2 = q*q; G = p->GSURF/q2; return( RGAS*TEMP/(G*XM) ); } @@ -1672,7 +1672,7 @@ double fDENSU, Z, ZG2, TT, TA, g, g2, DTA, Z1, Z2, T1, T2, ZG, ZGDIF; double YD1, YD2, X, Y, GLB, GAMMA, EXPL, DENSA, GAMM, YI; - //ZETA(ZZ, ZL) = (ZZ-ZL)*(p->RE+ZL)/(p->RE+ZZ); + //ZETA(ZZ, ZL) = (ZZ-ZL)*(p->Rref+ZL)/(p->Rref+ZZ); //////printf("DB %g %g %g %g %g %g %g %g %g %g %g\n", ALT, DLB, TINF, TLB, XM, ALPHA, ZLB, S2, MN1, ZN1, TN1 ); fDENSU = 1.0; @@ -1693,7 +1693,7 @@ double YD1, YD2, X, Y, GLB, GAMMA, EXPL, DENSA, GAMM, YI; if ( ALT < p->ZA ) { // CALCULATE TEMPERATURE BELOW ZA // Temperature gradient at ZA from Bates profile - g = (p->RE+ZLB)/(p->RE+p->ZA); g2 = g*g; + g = (p->Rref+ZLB)/(p->Rref+p->ZA); g2 = g*g; DTA = (TINF-TA)*S2*g2; TGN1[1] = DTA ; TN1[1] = TA; @@ -1716,7 +1716,7 @@ double YD1, YD2, X, Y, GLB, GAMMA, EXPL, DENSA, GAMM, YI; // End node derivatives YD1 = -TGN1[1]/(T1*T1)*ZGDIF; - g = (p->RE+Z2)/(p->RE+Z1); g2 = g*g; + g = (p->Rref+Z2)/(p->Rref+Z1); g2 = g*g; YD2 = -TGN1[2]/(T2*T2)*ZGDIF*g2; // Calculate spline coefficients @@ -1734,7 +1734,7 @@ double YD1, YD2, X, Y, GLB, GAMMA, EXPL, DENSA, GAMM, YI; if ( XM != 0.0 ) { // CALCULATE DENSITY ABOVE ZA - g = 1.0 + ZLB/p->RE; g2 = g*g; + g = 1.0 + ZLB/p->Rref; g2 = g*g; GLB = p->GSURF/g2; GAMMA = XM*GLB/(S2*RGAS*TINF); EXPL = exp(-S2*GAMMA*ZG2); @@ -1747,7 +1747,7 @@ double YD1, YD2, X, Y, GLB, GAMMA, EXPL, DENSA, GAMM, YI; if ( ALT < p->ZA ) { // CALCULATE DENSITY BELOW ZA - g = 1.0 + Z1/p->RE; g2 = g*g; + g = 1.0 + Z1/p->Rref; g2 = g*g; GLB = p->GSURF/g2; GAMM = XM*GLB*ZGDIF/RGAS; @@ -1784,7 +1784,7 @@ double DENSM( double ALT, double D0, double XM, double *TZ, int MN3, double *ZN3 double YD2, Y2OUT[11], X, Y, GLB, GAMM, YI, EXPL; // changed this fortran statement function to a macro - //ZETA(ZZ,ZL) = (ZZ-ZL)*(p->RE+ZL)/(p->RE+ZZ); + //ZETA(ZZ,ZL) = (ZZ-ZL)*(p->Rref+ZL)/(p->Rref+ZZ); fDENSM = D0; if ( ALT <= ZN2[1] ) { @@ -1807,7 +1807,7 @@ double DENSM( double ALT, double D0, double XM, double *TZ, int MN3, double *ZN3 YD1 = -TGN2[1]/(T1*T1)*ZGDIF; - g = (p->RE+Z2)/(p->RE+Z1); g2 = g*g; + g = (p->Rref+Z2)/(p->Rref+Z1); g2 = g*g; YD2 = -TGN2[2]/(T2*T2)*ZGDIF*g2; // Calculate spline coefficients @@ -1820,7 +1820,7 @@ double DENSM( double ALT, double D0, double XM, double *TZ, int MN3, double *ZN3 if ( XM != 0.0 ) { // CALCULATE STRATOSPHERE/MESOSPHERE DENSITY - g = 1.+Z1/p->RE; g2 = g*g; + g = 1.+Z1/p->Rref; g2 = g*g; GLB = p->GSURF/g2; GAMM = XM*GLB*ZGDIF/RGAS; @@ -1851,7 +1851,7 @@ double DENSM( double ALT, double D0, double XM, double *TZ, int MN3, double *ZN3 } YD1 = -TGN3[1]/(T1*T1)*ZGDIF; - g = (p->RE+Z2)/(p->RE+Z1); g2 = g*g; + g = (p->Rref+Z2)/(p->Rref+Z1); g2 = g*g; YD2 = -TGN3[2]/(T2*T2)*ZGDIF*g2; // Calculate spline coefficients @@ -1863,7 +1863,7 @@ double DENSM( double ALT, double D0, double XM, double *TZ, int MN3, double *ZN3 *TZ = 1.0/Y; if( XM != 0.0 ) { // CALCULATE TROPOSPHERIC/STRATOSPHERE DENSITY - g = 1.0 + Z1/p->RE; g2 = g*g; + g = 1.0 + Z1/p->Rref; g2 = g*g; GLB = p->GSURF/g2; GAMM = XM*GLB*ZGDIF/RGAS; diff --git a/libLanlGeoMag/Lgm_Trace.c b/libLanlGeoMag/Lgm_Trace.c index bd83e67cb..81518b6cc 100644 --- a/libLanlGeoMag/Lgm_Trace.c +++ b/libLanlGeoMag/Lgm_Trace.c @@ -114,6 +114,7 @@ int Lgm_Trace( Lgm_Vector *u, Lgm_Vector *v1, Lgm_Vector *v2, Lgm_Vector *v3, do Lgm_Vector u_scale, P, gpp; +u_scale.x = u_scale.y = u_scale.z = 1.0; /* * Determine our initial geocentric radius in km. (u is assumed to be in @@ -135,6 +136,7 @@ int Lgm_Trace( Lgm_Vector *u, Lgm_Vector *v1, Lgm_Vector *v2, Lgm_Vector *v3, do // must be inside the Earth, which is no good -- bail with // LGM_INSIDE_EARTH error code + if ( Info->VerbosityLevel > 2 ) { printf("Initial point is inside the Earth\n"); } return( LGM_INSIDE_EARTH ); } else if ( Rinitial < WGS84_A ) { @@ -148,6 +150,7 @@ int Lgm_Trace( Lgm_Vector *u, Lgm_Vector *v1, Lgm_Vector *v2, Lgm_Vector *v3, do if ( GeodHeight < 0.0 ) { // inside the Earth, which is no good -- bail with error + if ( Info->VerbosityLevel > 2 ) { printf("Initial point is inside the Earth\n"); } return( LGM_INSIDE_EARTH ); } @@ -228,6 +231,9 @@ Info->Hmax = 0.10; flag2 = Lgm_TraceToEarth( u, v2, Height, -sgn, TOL1, Info ); +if (Info->VerbosityLevel == -100){ +printf("u = %g %g %g v2, Height, sgn, TOL1 = %g %g %g, %g, %g, %g\n", u->x, u->y, u->z, v2->x, v2->y, v2->z, Height, sgn, TOL1); +} Info->Snorth = Info->Trace_s; // save distance from u to northern footpoint location. double MIKEA = Info->Trace_s; Info->v2_final = *v2; @@ -247,30 +253,83 @@ double MIKEB = Info->Trace_s; if ( flag1 && flag2 ) { + /* + * Pre-trace the FL to find the true global Bmin. This is necessary for FLs + * that have multiple minima on the -- probably wasteful otherwise. + * We could also add code to detect number of minima here. + * + * 1. Copy the Info structure so we dont mess anything up. + * 2. Start from the southern footpoint (v1) and tracev up to north (we know the distance.) + * 3. save the various info as well as how far we are in s. + */ + double tSS, tBmin, s_approx; + Lgm_Vector u_approx; + int ii, iBmin; + Lgm_MagModelInfo *Info2 = Lgm_CopyMagInfo( Info ); + +//printf("Info2->s[%d] = %g\n", iBmin, Info2->s[iBmin]); + tSS = Info->Snorth + Info->Ssouth; + Lgm_TraceLine3( v1, tSS, 500, 1.0, 1e-7, FALSE, Info2 ); + tBmin = 9e99; + iBmin = -1; + for (ii=0; iinPnts; ii++){ + if (Info2->Bmag[ii] <= tBmin) { + tBmin = Info2->Bmag[ii]; + iBmin = ii; + } + } + if ( (iBmin >= 0) && (iBmin < Info2->nPnts) ){ + u_approx.x = Info2->Px[iBmin]; + u_approx.y = Info2->Py[iBmin]; + u_approx.z = Info2->Pz[iBmin]; + s_approx = Info2->s[iBmin]; // distancev along FL from v1 to approximate global Bmin. + } else { + u_approx = *u; + s_approx = 0.0; + } +//printf("u_approx = %g %g %g\n", u_approx.x, u_approx.y, u_approx.z); +//printf("Info2->s[%d] = %g\n", iBmin, Info2->s[iBmin]); +//printf("Info2->Bmag[%d] = %g\n", iBmin-1, Info2->Bmag[iBmin-1]); +//printf("Info2->Bmag[%d] = %g\n", iBmin, Info2->Bmag[iBmin]); +//printf("Info2->Bmag[%d] = %g\n", iBmin+1, Info2->Bmag[iBmin+1]); + Lgm_FreeMagInfo( Info2 ); + + // We should probably put in a test here to see if the final point from + // TraceLine3() is different than what the other routines gives. + // This could be the basis for dynamically setting the tolerances. + + + + + /* * Closed FL -- attempt to trace to Eq Plane. */ - //Lgm_TraceToMinBSurf( v1, v3, TOL1, TOL2, Info ); - //Lgm_TraceToMinBSurf( v1, v3, 0.1, TOL2, Info ); - Lgm_TraceToMinBSurf( u, v3, 0.1, TOL2, Info ); + //Lgm_TraceToMinBSurf( u, v3, 0.1, TOL2, Info ); + Lgm_TraceToMinBSurf( &u_approx, v3, 0.1, TOL2, Info ); Info->v3_final = *v3; Info->Pmin = *v3; //Info->Smin = Info->Trace_s; // save location of Bmin. NOTE: Smin is measured from the southern footpoint. +//printf("About to call Info->Bfield( v3, &Bvec, Info );\n"); Info->Bfield( v3, &Bvec, Info ); +//printf("Done calling Info->Bfield( v3, &Bvec, Info );\n"); Info->Bvecmin = Bvec; Info->Bmin = Lgm_Magnitude( &Bvec ); - //printf("Bmin = %.15lf\n", Info->Bmin ); +// printf("Bmin = %.15lf\n", Info->Bmin ); +// printf("Info->Trace_s = %.15lf\n", Info->Trace_s ); +//printf("s_approx - Info->Trace_s = %g\n", s_approx - Info->Trace_s); +//exit(0); /* * Various FL arc lengths... * Snorth - distance from S/C to northern footpoint (set above) * Ssouth - distance from S/C to southern footpoint (set above) * Stotal - Total FL length ( Snorth + Ssouth ) (only compute for closed FL) - * Smin - distance from southern footpoint to S/C (Ssouth - what we - * got from Lgm_TraceToMinBSurf() because we started at S/C) + * Smin - Distance from southern footpoint to Pmin along the FL. (in Re). */ - Info->Stotal = Info->Snorth + Info->Ssouth; // Total FL length - Info->Smin = Info->Ssouth - Info->Trace_s; // length from south foot to S/C + Info->Stotal = Info->Snorth + Info->Ssouth; // Total FL length + //Info->Smin = Info->Ssouth - Info->Trace_s; // length from south foot to Pmin +Info->Smin = s_approx - Info->Trace_s; // length from south foot to Pmin Info->Trace_s = Info->Stotal; //printf("Info->Ssouth, Info->Trace_s = %g %g\n", Info->Ssouth, Info->Trace_s); @@ -356,8 +415,8 @@ double MIKEB = Info->Trace_s; */ Info->v1_final = *v1; Info->v2_final = *v2; - Info->Stotal = Info->Snorth + Info->Ssouth; // Total FL length within bounding box - Info->Trace_s = Info->Stotal; + Info->Stotal = Info->Snorth + Info->Ssouth; // Total FL length within bounding box + Info->Trace_s = Info->Stotal; return( LGM_OPEN_IMF ); diff --git a/libLanlGeoMag/Lgm_TraceToEarth.c b/libLanlGeoMag/Lgm_TraceToEarth.c index 4ab2f11df..30f756448 100644 --- a/libLanlGeoMag/Lgm_TraceToEarth.c +++ b/libLanlGeoMag/Lgm_TraceToEarth.c @@ -165,7 +165,7 @@ int Lgm_TraceToEarth( Lgm_Vector *u, Lgm_Vector *v, double TargetHeight, double /* * Save initial point if we need to */ - if (Info->SavePoints) fprintf(Info->fp, "%f \t%f\t %f\t 3\n", u->x, u->y, u->z); + if (Info->SavePoints) fprintf(Info->fp, "%.12lf \t%.12lf\t %.12f\t 3\n", u->x, u->y, u->z); @@ -304,7 +304,7 @@ int Lgm_TraceToEarth( Lgm_Vector *u, Lgm_Vector *v, double TargetHeight, double Lgm_Convert_Coords( &P, &w, GSM_TO_WGS84, Info->c ); Lgm_WGS84_to_GeodHeight( &w, &Height ); F = Height - TargetHeight; - if ((F > 0.0) && (Info->SavePoints)) fprintf(Info->fp, "%f \t%f\t %f\t 2\n", P.x, P.y, P.z); + if ((F > 0.0) && (Info->SavePoints)) fprintf(Info->fp, "%.12lf \t%.12lf\t %.12lf\t 2\n", P.x, P.y, P.z); if ( (P.x > Info->OpenLimit_xMax) || (P.x < Info->OpenLimit_xMin) || (P.y > Info->OpenLimit_yMax) || (P.y < Info->OpenLimit_yMin) || (P.z > Info->OpenLimit_zMax) || (P.z < Info->OpenLimit_zMin) || ( s > 1000.0 ) ) { @@ -444,7 +444,7 @@ Fc = eeFunc( &Pc, TargetHeight, Info); - if (Info->SavePoints) fprintf(Info->fp, "%f \t%f\t %f\t 2\n", v->x, v->y, v->z); + if (Info->SavePoints) fprintf(Info->fp, "%.12lf \t%.12lf\t %.12lf\t 2\n", v->x, v->y, v->z); if ( Info->VerbosityLevel > 2 ) printf("Lgm_TraceToEarth(): Number of Bfield evaluations = %ld\n", Info->Lgm_nMagEvals ); diff --git a/libLanlGeoMag/Lgm_TraceToMinBSurf2.c b/libLanlGeoMag/Lgm_TraceToMinBSurf2.c new file mode 100644 index 000000000..ce4edd41a --- /dev/null +++ b/libLanlGeoMag/Lgm_TraceToMinBSurf2.c @@ -0,0 +1,301 @@ +/*! \file Lgm_TraceToMinBSurf.c + * + * \brief Routines to find the Minimum-B surface along a field line. + * + * + * + * \author M.G. Henderson + * \date 1999 + * + * + * + */ + + +/* trace_test, Copyright (c) 1999 Michael G. Henderson + * + * + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + */ + +#include +#include +#include "Lgm/Lgm_MagModelInfo.h" + +int Lgm_TraceToMinBSurf( Lgm_Vector *u, Lgm_Vector *v, double Htry, double tol, Lgm_MagModelInfo *Info ) { + + Lgm_Vector u_scale; + double Htry_max, Hdid, Hnext, Hmin, Hmax, s=0.0, sgn, r2; + double Sa, Sb, Sc, d1, d2; + double Ba, Bb, Bc, B, B2, R; + Lgm_Vector Btmp; + Lgm_Vector Pa, Pb, Pc, P, P2; + int done, reset=TRUE; + double s2 = 0.0; + + + + Hmax = 0.5; +// Hmin = 0.00001; + Hmin = 1e-7; + Hmin = 1e-10; + u_scale.x = u_scale.y = u_scale.z = 1.0; + + + /* + * Bracket the minimum. We want to find two points along + * the field line such that the location of |B|_min is + * gauranteed to lie between them. To do this, we need to find + * three points; Pa, Pb, and Pc such that; + * + * Pc > Pb > Pa + * + * and |B( Pa )| > |B( Pb )| + * and |B( Pc )| > |B( Pb )| + * + * + */ + + done = FALSE; + Sa = Sb = Sc = 0.0; + Ba = Bb = Bc = 0.0; + + /* + * Set the start point, Pa and |B(Pa)|; + * I.e., Pa, Ba, Sa + */ + Pa = *u; + R = Lgm_Magnitude( &Pa ); + Info->Bfield( &Pa, &Btmp, Info ); + Ba = Lgm_Magnitude( &Btmp ); + Sa = 0.0; + +//printf("P, B (initial) = %g %g %g %g\n", Pa.x, Pa.y, Pa.z, Ba); + + /* + * Get an initial Htry that is safe -- i.e. start off slowly + * We dont really know where we are, so be conservative on the first try. + * If if ( Lgm_MagStep() gives back an Hnext thats higher, we'll crank Htry up then... + */ + Htry = 0.9*(R-1.0); // This computes Htry as 90% of the distance to the Earth's surface (could be small if we are already close!) + if (Htry > 0.01) Htry = 0.01; // If its bigger than 0.01 reset it to 0.01 -- to be safe. + + + + + + /* + * Determine which way to go.... + * Set Pb and |B(Pb)|. + * Try a step up the field line. If that doesnt give us + * a |B| less than Ba, then try down the field line. + * Use a fairly small stepsize to start with. + * This determined which direction we should move in (via value of sgn) + */ + P = Pa; + //reset = TRUE; + if ( Lgm_MagStep( &P, &u_scale, Htry, &Hdid, &Hnext, -1.0, &s, &reset, Info->Bfield, Info ) < 0 ) return(-1); + Info->Bfield( &P, &Btmp, Info ); + B = Lgm_Magnitude( &Btmp ); +//printf("NEG: P, B = %g %g %g %g\n", P.x, P.y, P.z, B); + + if ( B < Ba ) { + + Pb = P; + Bb = B; + Sb = Hdid; + sgn = -1.0; // We should move in direction opposite the field direction. + + } else { + + P2 = Pa; //reset = TRUE; + if ( Lgm_MagStep( &P2, &u_scale, Htry, &Hdid, &Hnext, 1.0, &s2, &reset, Info->Bfield, Info ) < 0 ) return(-1); + Info->Bfield( &P2, &Btmp, Info ); + B2 = Lgm_Magnitude( &Btmp ); +//printf("POS: P2, B2 = %g %g %g %g\n", P2.x, P2.y, P2.z, B2); + + if ( B2 < Ba ) { + Pb = P2; + Bb = B2; + Sb = Hdid; + sgn = 1.0; // We should move in direction with the field direction. + } else { + /* + * We must have already bracketed the min. + */ + Pb = Pa; Bb = Ba; Sb = Sa; + Pa = P; Ba = B; Sa = -s; + Pc = P2; Bc = B2; Sc = s2; + sgn = 1.0; + done = TRUE; + } + + } + +//printf("B = %g %g %g done = %d\n", Ba, Bb, Bc, done); + + + + + + + + + /* + * Keep stepping along the field line until we have a bracket. + */ + while (!done) { + + P = Pb; + if ( Lgm_MagStep( &P, &u_scale, Htry, &Hdid, &Hnext, sgn, &s, &reset, Info->Bfield, Info ) < 0 ) return(-1); + Info->Bfield( &P, &Btmp, Info ); + B = Lgm_Magnitude( &Btmp ); + + if ( B < Bb ) { + Pa = Pb; Ba = Bb; Sa = Sb; + Pb = P; Bb = B; Sb = Sa + Hdid; + if ( (P.x > Info->OpenLimit_xMax) || (P.x < Info->OpenLimit_xMin) || (P.y > Info->OpenLimit_yMax) || (P.y < Info->OpenLimit_yMin) + || (P.z > Info->OpenLimit_zMax) || (P.z < Info->OpenLimit_zMin) || ( s > 1000.0 ) ) { + /* + * Open FL! + */ + v->x = v->y = v->z = 0.0; + return(0); + } else { + Htry = Hnext; // adaptively reset Htry + + /* + * Dont attempt steps bigger than 0.25*distance to Earths surface + * Also respect Hmin and Hmax. + */ + R = Lgm_Magnitude( &P ); + Htry_max = 0.25*fabs(R-1.0); + if (Htry > Htry_max) Htry = Htry_max; + if (Htry < Hmin) Htry = Hmin; + else if (Htry > Hmax) Htry = Hmax; + } + } else { + Pc = P; Bc = B; Sc = Sb + Hdid; + done = TRUE; + } + + + } + + if ( (Bb > Bc) || (Bb > Ba) ) { + // no bracket + return(0); + } +//printf("Sa, Sb, Sc = %lf %lf %lf\n", Sa, Sb, Sc); +//printf("Ba, Bb, Bc = %lf %lf %lf\n", Ba, Bb, Bc); + + + + + /* + * We have a bracket. Now go in for the kill. + * Use golden-section search to converge toward minimum. + * (Sa, Sb, Sc) are the distances of the triple points along + * the FL. + */ +if (1==1){ + done = FALSE; +//reset=TRUE; + while (!done) { + + d1 = Sb - Sa; + d2 = Sc - Sb; + if ( (Sc-Sa) < tol ) { + + done = TRUE; + + } else if ( d1 > d2 ) { + + //P = Pa; Htry = 0.381966011*d1; + P = Pa; Htry = 0.5*d1; +//printf("A. Sa, Sb, Sc = %g %g %g d1, d2 = %g %g Htry = %g tol = %g Sc-Sa = %g\n", Sa, Sb, Sc, d1, d2, Htry, tol, Sc-Sa); + if ( Lgm_MagStep( &P, &u_scale, Htry, &Hdid, &Hnext, sgn, &s, &reset, Info->Bfield, Info ) < 0 ) return(-1); + Info->Bfield( &P, &Btmp, Info ); + B = Lgm_Magnitude( &Btmp ); +//printf("A. B = %g\n", B); + + if ( B < Bb ) { + Pc = Pb; Bc = Bb; Sc = Sb; + Pb = P; Bb = B; Sb = Sa + Hdid; + } else { + Pa = P; Ba = B; Sa += Hdid; + } + + } else { + + //P = Pb; Htry = 0.381966011*d2; + P = Pb; Htry = 0.5*d2; +//printf("B. Sa, Sb, Sc = %g %g %g d1, d2 = %g %g Htry = %g tol = %g Sc-Sa = %g\n", Sa, Sb, Sc, d1, d2, Htry, tol, Sc-Sa); + if ( Lgm_MagStep( &P, &u_scale, Htry, &Hdid, &Hnext, sgn, &s, &reset, Info->Bfield, Info ) < 0 ) return(-1); + Info->Bfield( &P, &Btmp, Info ); + B = Lgm_Magnitude( &Btmp ); +//printf("B. P = %g %g %g B = %g\n", P.x, P.y, P.z, B); + + if ( B < Bb ) { + Pa = Pb; Ba = Bb; Sa = Sb; + Pb = P; Bb = B; Sb += Hdid; + } else { + Pc = P; Bc = B; Sc = Sb + Hdid; + } + + } + + } +} + +//reset = TRUE; + + /* + * Try Brent's method +THIS DOESNT SEEM TO GIVE RESULTS THAT ARE AS GOOD. FIND OUT WHY.... + */ +if (0==1){ +//printf("Sa, Sb, Sc = %g %g %g Ba, Bb, Bc = %g %g %g tol = %g\n", Sa, Sb, Sc, Ba, Bb, Bc, tol); + double Smin, Bmin; + Lgm_Vector Pmin; + BrentFuncInfoP f; + + f.u_scale = u_scale; + f.Htry = Htry; + f.sgn = sgn; + f.reset = reset; + f.Info = Info; + Lgm_BrentP( Sa, Sb, Sc, Bb, Pa, Pb, Pc, &f, tol, &Smin, &Bmin, &Pmin ); + Bb = Bmin; + Sb = Smin; + Pb = Pmin; +//printf("Sa, Sb, Sc = %g %g %g Ba, Bb, Bc = %.15lf %.15lf %.15lf tol = %g\n", Sa, Sb, Sc, Ba, Bb, Bc, tol); +} + + + + + + /* + * Take location of the Min-B surface to be Pb. + */ + *v = Pb; + + //Info->Trace_s = Sb*sgn; + Info->Trace_s = -Sb*sgn; + + if ( Info->VerbosityLevel > 2 ) printf("TraceToMinBSurf(): Number of Bfield evaluations = %ld\n", Info->Lgm_nMagEvals ); + + return( 1 ); + +} + + diff --git a/libLanlGeoMag/MagStep.c b/libLanlGeoMag/MagStep.c index db5e6a665..d18ac6669 100644 --- a/libLanlGeoMag/MagStep.c +++ b/libLanlGeoMag/MagStep.c @@ -349,7 +349,7 @@ int Lgm_MagStep_BS( Lgm_Vector *u, Lgm_Vector *u_scale, rtol = Info->Lgm_MagStep_BS_rtol; - if ( ( eps != Info->Lgm_MagStep_BS_eps_old ) || ( *reset ) ){ + if ( ( eps != Info->Lgm_MagStep_BS_eps_old ) || ( *reset ) ) { Info->Lgm_nMagEvals = 0; Info->Lgm_MagStep_BS_eps_old = eps; diff --git a/libLanlGeoMag/Makefile.am b/libLanlGeoMag/Makefile.am index dc7b3a465..e4f07a93d 100644 --- a/libLanlGeoMag/Makefile.am +++ b/libLanlGeoMag/Makefile.am @@ -36,7 +36,7 @@ endif #libdir = @prefix@/lib lib_LTLIBRARIES = libLanlGeoMag.la libLanlGeoMag_la_SOURCES = Lgm_AlphaOfK.c Lgm_DFI_RBF.c Lgm_Vec_RBF.c Lgm_B_FromScatteredData.c ComputeLstar.c DriftShell.c IntegralInvariant.c LFromIBmM.c \ - Lgm_B_internal.c Lgm_CTrans.c Lgm_DateAndTime.c Lgm_Eop.c Lgm_IGRF.c Lgm_InitMagInfo.c \ + Lgm_B_internal.c Lgm_CTrans.c Lgm_DateAndTime.c Lgm_Eop.c Lgm_IGRF.c Lgm_InitMagInfo.c FluxTubeVolume.c\ Lgm_MaxwellJuttner.c Lgm_Nutation.c Lgm_Octree.c Lgm_Quat.c Lgm_Sgp.c Lgm_SimplifiedMead.c Lgm_SunPosition.c \ Lgm_Trace.c Lgm_TraceToEarth.c Lgm_TraceToSphericalEarth.c Lgm_Vec.c MagStep.c Lgm_QuadPack3.c \ Lgm_QuadPack.c Lgm_Cgm.c quicksort.c SbIntegral.c T87.c T89.c T89c.c TraceLine.c Lgm_TraceToMinBSurf.c \ @@ -45,10 +45,10 @@ libLanlGeoMag_la_SOURCES = Lgm_AlphaOfK.c Lgm_DFI_RBF.c Lgm_Vec_RBF.c Lgm_B_Fro W.c Lgm_InitMagEphemInfo.c Lgm_AE8_AP8.c OP77.c OP88.c OlsenPfitzerDynamic.c OlsenPfitzerStatic.c IsoTimeStringToDateTime.c \ size.c Lgm_FluxToPsd.c xvgifwr2.c praxis.c Lgm_SphHarm.c Lgm_McIlwain_L.c Lgm_ElapsedTime.c Lgm_KdTree.c\ Lgm_ComputeLstarVersusPA.c Lgm_MagEphemWrite.c Lgm_MagEphemWriteHdf.c brent.c Lgm_CdipMirrorLat.c ComputeI_FromMltMlat.c ComputeI_FromMltMlat2.c \ - Lgm_QinDenton.c Lgm_DiffCoeff_param.c Lgm_AE_index.c Lgm_Misc.c Lgm_HDF5.c Lgm_GradB.c Lgm_VelStep.c Lgm_Utils.c DynamicMemory.h \ + Lgm_QinDenton.c Lgm_DiffCoeff_param.c Lgm_AE_index.c Lgm_Misc.c Lgm_HDF5.c Lgm_GradB.c Lgm_GradBvec.c Lgm_GradBvec2.c Lgm_VelStep.c Lgm_Utils.c DynamicMemory.h \ Lgm_Metadata.c Lgm_PriorityQueue.c TraceToYZPlane.c Lgm_InitNrlMsise00.c Lgm_NrlMsise00.c Lgm_Coulomb.c\ Lgm_Ellipsoid.c Lgm_DipEquator.c \ - Lgm_JPLephem.c Lgm_Eclipse.c Lgm_TabularBessel.c + Lgm_JPLephem.c Lgm_Eclipse.c Lgm_TabularBessel.c GPR_v3.c Lgm_FirstInvariant.c diff --git a/libLanlGeoMag/OlsenPfitzerDynamic.c b/libLanlGeoMag/OlsenPfitzerDynamic.c old mode 100755 new mode 100644 diff --git a/libLanlGeoMag/T89c.c b/libLanlGeoMag/T89c.c old mode 100755 new mode 100644 diff --git a/libLanlGeoMag/TS04.c b/libLanlGeoMag/TS04.c index 72f9e7ba4..82a6d5649 100644 --- a/libLanlGeoMag/TS04.c +++ b/libLanlGeoMag/TS04.c @@ -3,7 +3,7 @@ int Lgm_B_TS04( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ) { Lgm_Vector B2; int iopt; - double parmod[11], ps, Bmag, X, Y, Z, Bx, By, Bz; + double parmod[11], ps, Bmag, X, Y, Z, R2, Bx, By, Bz; double FEXT, FINT, BBX, BBY, BBZ, OIMFX, OIMFY, OIMFZ; @@ -82,6 +82,12 @@ int Lgm_B_TS04( Lgm_Vector *v, Lgm_Vector *B, Lgm_MagModelInfo *Info ) { * access to our dipole model. * */ + R2 = X*X + Y*Y + Z*Z; + if ( R2 < 0.5 ) { + printf("Inside Earth? X, Y, Z = %g %g %g\n", X, Y, Z); + return(-1); + } + Tsyg_TS04( iopt, parmod, ps, Info->c->sin_psi, Info->c->cos_psi, X, Y, Z, &Bx, &By, &Bz, &Info->TS04_Info ); switch ( Info->InternalModel ){ diff --git a/libLanlGeoMag/TraceLine.c b/libLanlGeoMag/TraceLine.c index 575a10edd..57f0cc374 100644 --- a/libLanlGeoMag/TraceLine.c +++ b/libLanlGeoMag/TraceLine.c @@ -1003,8 +1003,7 @@ double BofS( double s, Lgm_MagModelInfo *Info ) { */ if ( (s < Info->s[0]) || (s > Info->s[Info->nPnts-1]) ) { printf("BofS: ( Line %d in file %s ). Trying to evaluate BofS( s, Info ) for an s that is outside of the bounds of the interpolating arrays.\n\tInfo->nPnts = %d, Info->s[0] = %.8g, Info->s[%d] = %.8g, s = %.8g\n", __LINE__, __FILE__, Info->nPnts, Info->s[0], Info->nPnts-1, Info->s[Info->nPnts-1], s); -raise(6); -// exit(-1); + raise(6); } @@ -1319,12 +1318,17 @@ int Lgm_TraceLine3( Lgm_Vector *u, double S, int N, double sgn, double tol, int } Hsum += Hdid; - if ( fabs(Hdid-Htry) < 1e-7 ) { +//if ( nSubSteps > 100 ){ +//printf("R = %lf km ( %lf Re) P = %g %g %g Hdid, Htry, Hnext = %g %g %g AHA Problem in TraceLine3(). File: %s, Line %d. Too many substeps. nSubSteps = %d\n", Re*Lgm_Magnitude(&P), Lgm_Magnitude(&P), P.x, P.y, P.z, Hdid, Htry, Hnext, __FILE__, __LINE__, nSubSteps ); +//} + //if ( fabs(Hdid-Htry) < 1e-7 ) { + if ( fabs(Hsum-Htry0) < 1e-7 ) { // we got what we asked for. DoneStep = TRUE; - } else if ( nSubSteps > 100 ) { + //} else if ( nSubSteps > 100 ) { + } else if ( nSubSteps > 1000 ) { DoneStep = TRUE; - printf("Problem in TraceLine3(). File: %s, Line %d. Too many substeps.\n", __FILE__, __LINE__ ); + printf("Problem in TraceLine3(). File: %s, Line %d. Too many substeps. nSubSteps = %d\n", __FILE__, __LINE__, nSubSteps ); return(-1); } else { // we did not get what we asked for. Try to step the remainder. @@ -1392,7 +1396,20 @@ int Lgm_TraceLine3( Lgm_Vector *u, double S, int N, double sgn, double tol, int * than the S that was requested. This can happen (for example) if S is * already very small, and the tolerances arent small enough to get there * precisely enough (maybe related to getting near machine precision + - * round off errors etc.). + * round off errors etc.). + * + * Another usual suspect here is that the atol, rtol tolerance for MagStep + * are too low. The default values of; + * + * Info->Lgm_MagStep_BS_atol = 1e-5 + * Info->Lgm_MagStep_BS_rtol = 0.0 + * + * can often lead to mismatches in tracing between Lgm_Trace and + * Lgm_TraceLineX() routines. Return with a unique error code (-2) to + * indicate that maybe the user should increase the tols. + * + * + * * * At any rate, we must guard against this, because BofS() will abort if * you try to evaluate it outside of the defined range of s's. @@ -1409,27 +1426,16 @@ int Lgm_TraceLine3( Lgm_Vector *u, double S, int N, double sgn, double tol, int * ss is the total distance traced so far * If we get here we've determined that we're done tracing, for whatever reason... */ - if (Info->VerbosityLevel > 1) printf("Trace did not get to requested endpoint: Target (S), Actual (ss), S-ss = %g %g %g\n", S, ss, S-ss); - return(-1); + if (Info->VerbosityLevel > 1) { + printf("Trace did not get to requested endpoint: Target (S), Actual (ss), S-ss = %g %g %g\n", S, ss, S-ss); + } + return(-2); } - - - - - - - - - - - - - /* * Add the Smin, Bmin point. Only do this if AddBminPoint is TRUE * This will only make sense if these values are legitimate for this FL. diff --git a/libLanlGeoMag/Tsyg2004.c b/libLanlGeoMag/Tsyg2004.c index 8c1269f9c..3fbe5e667 100644 --- a/libLanlGeoMag/Tsyg2004.c +++ b/libLanlGeoMag/Tsyg2004.c @@ -182,11 +182,15 @@ void Tsyg_TS04( int IOPT, double *PARMOD, double PS, double SINPS, double COSPS, ZZ = Z; +//printf("About to call TS04_EXTERN(). PDYN, DST_AST, BXIMF, BYIMF, BZIMF, W1, W2, W3, W4, W5, W6, PSS, XX, YY, ZZ = %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n", PDYN, DST_AST, BXIMF, BYIMF, BZIMF, W1, W2, W3, W4, W5, W6, PSS, XX, YY, ZZ); + + TS04_EXTERN( IOPGEN, IOPTT, IOPB, IOPR, A, 69, PDYN, DST_AST, BXIMF, BYIMF, BZIMF, W1, W2, W3, W4, W5, W6, PSS, XX, YY, ZZ, &BXCF, &BYCF, &BZCF, &BXT1, &BYT1, &BZT1, &BXT2, &BYT2, &BZT2, &BXSRC, &BYSRC, &BZSRC, &BXPRC, &BYPRC, &BZPRC, &BXR11, &BYR11, &BZR11, &BXR12, &BYR12, &BZR12, &BXR21, &BYR21, &BZR21, &BXR22, &BYR22, &BZR22, &HXIMF, &HYIMF, &HZIMF, &BBX, &BBY, &BBZ, tInfo ); +//printf("Done calling TS04_EXTERN()\n"); /* printf("BXCF, BYCF, BZCF = %.10g %.10g %.10g\n", BXCF, BYCF, BZCF ); @@ -256,6 +260,7 @@ void TS04_EXTERN( int IOPGEN, int IOPT, int IOPB, int IOPR, double *A, int NTOT, double A0_A=34.586, A0_S0=1.1960, A0_X0=3.4397; // SHUE ET AL. PARAMETERS double DSIG=0.005, RH2=-5.2; +//printf("X, Y, Z = %g %g %g\n", X, Y, Z); tInfo->CB_G.G = 35.0; // TAIL WARPING PARAMETER tInfo->CB_RH0.RH0 = 7.5; // TAIL HINGING DISTANCE @@ -306,6 +311,7 @@ void TS04_EXTERN( int IOPGEN, int IOPT, int IOPB, int IOPR, double *A, int NTOT, * BEGIN ITERATIVE SEARCH OF UNWARPED COORDS (TO FIND SIGMA) */ done = FALSE; +//printf("About to enter a while loop\n"); while ( !done ) { XSOLD = XSS; @@ -324,10 +330,12 @@ void TS04_EXTERN( int IOPGEN, int IOPT, int IOPB, int IOPR, double *A, int NTOT, ZSS = X*SINPSAS + Z*COSPSAS; XSS = X*COSPSAS - Z*SINPSAS; DD = fabs( XSS-XSOLD ) + fabs( ZSS-ZSOLD ); +//printf("XSOLD, ZSOLD, R, ZSSoR2, RH, RoRH, SINPSAS, COSPSAS, ZSS, XSS, DD = %g %g %g %g %g %g %g %g %g %g %g\n", XSOLD, ZSOLD, R, ZSSoR2, RH, RoRH, SINPSAS, COSPSAS, ZSS, XSS, DD); if ( DD <= 1e-6 ) done = TRUE; } +//printf("Exited while loop\n"); RHO2 = Y*Y + ZSS*ZSS; diff --git a/libLanlGeoMag/quicksort.c b/libLanlGeoMag/quicksort.c index 283dc4b48..7bc1c093a 100644 --- a/libLanlGeoMag/quicksort.c +++ b/libLanlGeoMag/quicksort.c @@ -6,7 +6,7 @@ #define NSTACK 2001 /* - * These routines assume the retarded NR index starts at 1 thing. + * These routines assume the silly NR index starts at 1 thing. */ void quicksort( unsigned long n, double *arr ) { @@ -15,6 +15,8 @@ void quicksort( unsigned long n, double *arr ) { int jstack=0, istack[NSTACK]; double a, temp; + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; for (;;) { if (ir-l < M) { @@ -81,6 +83,8 @@ void quicksort_uli( unsigned long n, unsigned long *arr ) { int jstack=0, istack[NSTACK]; unsigned long a, temp; + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; for (;;) { if (ir-l < M) { @@ -148,6 +152,8 @@ void quicksort2( unsigned long n, double *arr, double *brr ) { int jstack=0, istack[NSTACK]; double a, b, temp; + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; for (;;) { if (ir-l < M) { @@ -225,6 +231,8 @@ void quicksort2uli( unsigned long n, double *arr, unsigned long int *brr ) { int b; double a, temp; + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; for (;;) { if (ir-l < M) { @@ -301,6 +309,8 @@ void quicksort3( unsigned long n, double *arr, double *brr, double *crr ) { int jstack=0, istack[NSTACK]; double a, b, c, temp; + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; for (;;) { if (ir-l < M) { @@ -386,18 +396,18 @@ void quicksort3( unsigned long n, double *arr, double *brr, double *crr ) { * An implementation of bubbleSort in case a user wants bubbleSort instead of quicksort * BAL - 20Apr2011 */ -void bubbleSort(unsigned long n, double *arr) -{ - long i, j, temp; - - for (i=(n-1); i>0; i--) /* loop over first element */ - { - for (j=1; j<=i; j++) /* loop over second element */ - { - if (arr[j-1] > arr[j]) - { - SWAP(arr[j-1],arr[j]) - } +void bubbleSort(unsigned long n, double *arr) { + + long i, j, temp; + + // This should really cause a 0 on return -- change function type to int? + if ( n <= 0 ) return; + + for (i=(n-1); i>0; i--) {/* loop over first element */ + for (j=1; j<=i; j++) {/* loop over second element */ + if (arr[j-1] > arr[j]) { + SWAP(arr[j-1],arr[j]) + } + } } - } }