updateMadDerivations.py
1807 lines
| 54.5 KiB
| text/x-python
|
PythonLexer
r0 | #!$MADROOT/bin/python | ||
"""updateMadDerivations.py is a script that autogenerates ../../madrigal/_derive_new.c | |||
based on the latest methods in source/madpy/madrigal/derivation.py. | |||
Run after derivation.py has been updated. Must be run from madpy/scripts/bin directory | |||
$Id: updateMadDerivations.py 7046 2019-10-07 19:57:14Z brideout $ | |||
""" | |||
# standard python imports | |||
import os | |||
# madrigal imports | |||
import madrigal.derivation | |||
# verify we are in the right directory | |||
pwd = os.getcwd() | |||
if not pwd.endswith('source/madpy/scripts/bin'): | |||
raise IOError('This script must be run from source/madpy/scripts/bin, not %s' % (pwd)) | |||
codeTemplate1 = """ if (strcmp(methodName, "%s")==0) | |||
%s(inLen, inValues, outLen, outValues, stderr); | |||
""" | |||
codeTemplate2 = """ else if (strcmp(methodName, "%s")==0) | |||
%s(inLen, inValues, outLen, outValues, stderr); | |||
""" | |||
# first template - start of code | |||
startTemplate = """/* | |||
* This code exists to expose all the calculation methods in madDeriveMethods via | |||
* a single python call: madDispatch. madDispatch takes three arguments: | |||
* 1. a string name of the function to call | |||
* 2. a list of floats as inputs to the call | |||
* 3. a list of floats to be set by the call | |||
* | |||
* This file is autogenerated by updateMadDerivations.py. Please | |||
* do not directly edit this file. updateMadDerivations.py takes | |||
* as input the derivation.py file, and creates C code that | |||
* matches all calls in it. The only files that are manually edited | |||
* when a new method is added are derivation.py and | |||
* madDeriveMethods.[c,h] | |||
* | |||
* This file also contains some help methods to expose fuctionality of | |||
* the geolib. However, again, this file should not be directly modified | |||
* in Subversion - instead, the new helper code must be added to | |||
* updateMadDerivations.py, and this file reautogenerated. | |||
* | |||
* $Id: updateMadDerivations.py 7046 2019-10-07 19:57:14Z brideout $ | |||
*/ | |||
#include <Python.h> | |||
#include <numpy/ndarrayobject.h> | |||
#include <madDeriveMethods.h> | |||
#include <date.h> | |||
/*********************************************************************** | |||
* | |||
* madDispatch runs an underlying C derivation method | |||
* | |||
* arguments: | |||
* 1. Python string representing name of method to call | |||
* 2. Python double 1D numpy vector representing the input arguments | |||
* 3. Python double 1D numpy vector representing the output - will be modified | |||
* | |||
* returns: | |||
* Python int 0 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_madDispatch(PyObject * self, PyObject * args) | |||
{ | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
int inLen = 0, outLen = 0; | |||
double * inValues; | |||
double * outValues; | |||
/* input variables */ | |||
char * methodName = NULL; | |||
PyArrayObject *inArr; | |||
PyArrayObject *outArr; | |||
if (!PyArg_ParseTuple(args, "sO!O!", | |||
&methodName, | |||
&PyArray_Type, &inArr, | |||
&PyArray_Type, &outArr)) | |||
return NULL; | |||
/* check that one-D arrays */ | |||
if (inArr->nd != 1 || outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions in in and out arrays must be 1, not %i and %i", | |||
inArr->nd, outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
inLen = inArr->dimensions[0]; | |||
outLen = outArr->dimensions[0]; | |||
inValues = (double *)inArr->data; | |||
outValues = (double *)outArr->data; | |||
/* call the right C method - the following code is autogenerated */ | |||
""" | |||
# end template | |||
endTemplate = """ /* end autogenerated code */ | |||
else | |||
{ | |||
sprintf(errBuf, "methodName %s not found", methodName); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/* methods to call fortran/C methods from python */ | |||
/*********************************************************************** | |||
* | |||
* madIsrim runs Shunrong's underlying Fortran method isrim | |||
* | |||
* arguments: | |||
* 1. Python double representing UT1 | |||
* 2. Python int representing kinst | |||
* 3. Python double represent solar local time | |||
* 4. Python double representing GDALT in km | |||
* 5. Python double representing GDLAT in degrees | |||
* 6. Python double representing F107 24 hours before UT1 in W/m2/Hz | |||
* 7. Python double representing AP3 3 hours before | |||
* 8. Python int representing IPAR flag | |||
* 1, FOR NE | |||
* 2, FOR TE | |||
* 3, FOR TI | |||
* 4, FOR PARALLEL ION DRIFT, + UPWARD | |||
* 5, for HMAX and NMAX | |||
* 9. Python double 1D numpy vector size (4,) representing the output - will be modified | |||
* | |||
* returns: | |||
* Python int 0 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_madIsrim(PyObject * self, PyObject * args) | |||
{ | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
double * outValues; | |||
int iyr=0, imd=0, ihm=0, ics=0; | |||
int month=0, day=0, i=0; | |||
double doy = 0.0; /* day of year (1-366) */ | |||
int ipar2; | |||
/* outputs of Shunrong's model */ | |||
double output[4]; | |||
/* input variables */ | |||
double ut1; | |||
int kinst; | |||
double slt; | |||
double gdalt; | |||
double gdlat; | |||
double f107; | |||
double ap3; | |||
int ipar; | |||
PyArrayObject *outArr; | |||
/* init */ | |||
for (i=0; i<4; i++) | |||
output[i] = 0.0; /* also used as an input by Shunrong's code */ | |||
if (!PyArg_ParseTuple(args, "didddddiO!", | |||
&ut1, &kinst, &slt, &gdalt, | |||
&gdlat, &f107, &ap3, &ipar, | |||
&PyArray_Type, &outArr)) | |||
return NULL; | |||
/* check that one-D array */ | |||
if (outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of out array must be 1, not %i", | |||
outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
if (ipar == 5) | |||
{ | |||
/* set to get hmax and nmax */ | |||
output[3] = 1.0; | |||
ipar2 = 1; /* ne required to be set */ | |||
} | |||
else | |||
ipar2 = ipar; | |||
outValues = (double *)outArr->data; | |||
/* get doy */ | |||
dinvmadptr(ut1, &iyr, &imd, &ihm, &ics); | |||
month = imd/100; | |||
day = imd - (100*month); | |||
doy = (double)madGetDayno(iyr, month, day); | |||
ISRIM_F77(&kinst, &doy, &slt, &gdalt, &gdlat, &f107, &ap3, &ipar2, output); | |||
if (ipar == 1) | |||
{ | |||
/* handle Ne case */ | |||
if ((output[3] < 0.0) || (output[0] <= 0.0)) | |||
{ | |||
outValues[0] = NAN; | |||
outValues[1] = NAN; | |||
} | |||
else | |||
{ | |||
outValues[0] = output[0]; | |||
outValues[1] = log10(output[0]); | |||
} | |||
} | |||
else if (ipar == 5) | |||
{ | |||
/* handle Hmax, Nmax case */ | |||
if (output[1] < 1.0) | |||
{ | |||
outValues[0] = NAN; | |||
outValues[1] = NAN; | |||
} | |||
else | |||
{ | |||
outValues[0] = output[1]; | |||
outValues[1] = output[2]; | |||
} | |||
} | |||
else /* all others just return one value */ | |||
{ | |||
if (output[3] < 0.0) | |||
outValues[0] = NAN; | |||
else | |||
outValues[0] = output[0]; | |||
} | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* madRunIri runs underlying IRI methods via c method run_iri_3 | |||
* | |||
* arguments: | |||
* 1-6. Python ints representing year, month, day, hour, min, sec | |||
* 7. Python double representing GDLAT in degrees | |||
* 8. Python double representing GLON in degrees | |||
* 9. Python double representing GDALT in km | |||
* 10. Numpy int32 array of length 13 with 13 previous values of ap3 (last is present value) | |||
* 11. Python double representing F107 in W/m2/Hz | |||
* 12. Python double 1D numpy vector representing the 11 outputs: | |||
* NE_IRI Electron density in #/meter3. units: m-3 | |||
* NEL_IRI Log of electron density in #/meter3. units:log10(m-3) | |||
* TN_IRI IRI Neutral temperature | |||
* TI_IRI IRI Ion temperature | |||
* TE_IRI IRI Electron temperature | |||
* PO+_IRI IRI Composition - [O+]/Ne | |||
* PNO+_IRI IRI Composition - [NO+]/Ne | |||
* PO2+_IRI IRI Composition - [O2+]/Ne | |||
* PHE+_IRI IRI Composition - [HE+]/Ne | |||
* PH+_IRI IRI Composition - [H+]/Ne | |||
* PN+_IRI IRI Composition - [N+]/Ne | |||
* | |||
* returns: | |||
* Python int 0 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_madRunIri(PyObject * self, PyObject * args) | |||
{ | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
int * ap3Values; | |||
double * outValues; | |||
/* input variables */ | |||
int year, month, day, hour, minute, second; | |||
double gdlat, glon, gdalt; | |||
double f107; | |||
PyArrayObject *ap3Arr, *outArr; | |||
if (!PyArg_ParseTuple(args, "iiiiiidddO!dO!", | |||
&year, &month, &day, &hour, &minute, &second, | |||
&gdlat, &glon, &gdalt, | |||
&PyArray_Type, &ap3Arr, | |||
&f107, | |||
&PyArray_Type, &outArr)) | |||
return NULL; | |||
/* check that one-D array */ | |||
if (ap3Arr->nd != 1 || outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of ap3 or out array must be 1, not %i and %i", | |||
ap3Arr->nd, outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
ap3Values = (int *)ap3Arr->data; | |||
outValues = (double *)outArr->data; | |||
run_iri_3(year, month, day, hour, minute, second, | |||
gdlat, glon, gdalt, | |||
ap3Values, f107, outValues); | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* madRunMsis runs underlying MSIS methods via fortran method gtd7d | |||
* | |||
* arguments: | |||
* 1-6. Python ints representing year, month, day, hour, min, sec | |||
* 7. Python double representing GDLAT in degrees | |||
* 8. Python double representing GLON in degrees | |||
* 9. Python double representing GDALT in km | |||
* 10. Numpy double array of length 7 with AP data from present and up to 57 hours prior to | |||
* present. (see gtd7d for algorithm) | |||
* 11. Python double representing FBAR in 1.0e-22 W/m2/Hz | |||
* 12. Python double representing F107 24 hours previous in 1.0e-22 W/m2/Hz | |||
* 13. Python double 1D numpy vector representing the 26 outputs: | |||
* TNM - Model neutral atmosphere temperature | |||
* TINFM - Model Exospheric temperature | |||
* MOL - Log10 (nutrl atm mass density in kg/m3) | |||
* NTOTL - Log10 (nutrl atm number density in m-3) | |||
* NN2L - Nutrl atm compositn-log10([N2] in m-3) | |||
* NO2L - Nutrl atm compositn-log10([O2] in m-3) | |||
* NOL - Nutrl atm composition-log10([O] in m-3) | |||
* NARL - Nutrl atm compositn-log10([AR] in m-3) | |||
* NHEL - Nutrl atm compositn-log10([HE] in M-3) | |||
* NHL - Nutrl atm composition-log10([H] in m-3) | |||
* NN4SL - Nutrl atm compstn-log10([N(4S)] in m-3) | |||
* NPRESL - Neutral atmospher log10(pressure in Pa) | |||
* PSH - Pressure scale height (m) | |||
* DTNM - Error in Model neutral atmosphere temperature | |||
* DTINFM - Error in Model Exospheric temperature | |||
* DMOL - Error in Log10 (nutrl atm mass density in kg/m3) | |||
* DNTOTL - Error in Log10 (nutrl atm number density in m-3) | |||
* DNN2L - Error in Nutrl atm compositn-log10([N2] in m-3) | |||
* DNO2L - Error in Nutrl atm compositn-log10([O2] in m-3) | |||
* DNOL - Error in Nutrl atm composition-log10([O] in m-3) | |||
* DNARL - Error in Nutrl atm compositn-log10([AR] in m-3) | |||
* DNHEL - Error in Nutrl atm compositn-log10([HE] in M-3) | |||
* DNHL - Error in Nutrl atm composition-log10([H] in m-3) | |||
* DNN4SL - Error in Nutrl atm compstn-log10([N(4S)] in m-3) | |||
* DNPRESL - Error in Neutral atmospher log10(pressure in Pa) | |||
* DPSH - Error in Pressure scale height (m) | |||
* | |||
* returns: | |||
* Python int 0 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_madRunMsis(PyObject * self, PyObject * args) | |||
{ | |||
/* constants */ | |||
const double k = 1.3807e-23; | |||
const double g = 9.8; | |||
const double errPercent = 0.2; /* assumed error percentage */ | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
double * apValues; | |||
double * outValues; | |||
double ave_mass = 0.0; | |||
int i = 0; | |||
/* declaration of data to be passed into gtd7d */ | |||
int iyd = 0; /* date in form YYDDD */ | |||
double sec = 0.0; /* seconds since beginning of UT day */ | |||
double slt = 0.0; /* slt=sec/3600+glon/15 */ | |||
int mass = 48; /* tells gtd7d to calculate all */ | |||
/* declaration of data returned from gtd7d */ | |||
double d[9]; | |||
double t[2]; | |||
/* input variables */ | |||
int year, month, day, hour, minute, second; | |||
double gdlat, glon, gdalt; | |||
double f107, fbar; | |||
PyArrayObject *apArr, *outArr; | |||
/* init arrays to keep purify happy */ | |||
for (i=0; i<9; i++) | |||
d[i] = 0.0; | |||
for (i=0; i<2; i++) | |||
t[i] = 0.0; | |||
if (!PyArg_ParseTuple(args, "iiiiiidddO!ddO!", | |||
&year, &month, &day, &hour, &minute, &second, | |||
&gdlat, &glon, &gdalt, | |||
&PyArray_Type, &apArr, | |||
&fbar, &f107, | |||
&PyArray_Type, &outArr)) | |||
return NULL; | |||
/* check that one-D array */ | |||
if (apArr->nd != 1 || outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of apArr or out array must be 1, not %i and %i", | |||
apArr->nd, outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* force glon to between -180 and 180 */ | |||
while (glon < -180.0) glon += 360.0; | |||
while (glon > +180.0) glon -= 360.0; | |||
/* weird time input calculations */ | |||
iyd = (year%100)*1000; /* set up year part of YYDDD */ | |||
iyd += madGetDayno(year, month, day); /* set up day part of YYDDD */ | |||
sec = hour*3600.0; | |||
sec += minute*60.0; | |||
slt = sec/3600.0 + glon/15.0; | |||
apValues = (double *)apArr->data; | |||
outValues = (double *)outArr->data; | |||
/* call main MSIS method */ | |||
GTD7D_F77(&iyd, | |||
&sec, | |||
&gdalt, | |||
&gdlat, | |||
&glon, | |||
&slt, | |||
&fbar, | |||
&f107, | |||
apValues, | |||
&mass, | |||
d, | |||
t); | |||
/* get average mass in kg */ | |||
if (d[0]+d[1]+d[2]+d[3]+d[4]+d[6]+d[7] > 0.0 && d[5] > 0.0) | |||
{ | |||
ave_mass = d[5]/(d[0]+d[1]+d[2]+d[3]+d[4]+d[6]+d[7]); | |||
/* convert from grams to kg */ | |||
ave_mass = ave_mass*1.e-3; | |||
/* get pressure */ | |||
outValues[11]=(d[0]+d[1]+d[2]+d[3]+d[4]+d[6]+d[7])*1.e6*k*t[1]; | |||
/* get scale height */ | |||
outValues[12]=k*t[1]/(ave_mass*g); | |||
} | |||
else | |||
{ | |||
outValues[11]=NAN; | |||
outValues[12]=NAN; | |||
} | |||
/* copy results */ | |||
outValues[0] = t[1]; /* tnm */ | |||
outValues[1] = t[0]; /* tinfm */ | |||
if (d[5] > 0.0) | |||
outValues[2] = log10(d[5]*1.e3); /* mol */ | |||
else | |||
outValues[2] = NAN; | |||
if (d[0]+d[1]+d[2]+d[3]+d[4]+d[6]+d[7] > 0.0) | |||
outValues[3] = log10(1.e6*(d[0]+d[1]+d[2]+d[3]+d[4]+d[6]+d[7])); /* neutral density */ | |||
else | |||
outValues[3] = NAN; | |||
if (d[2] > 0.0) | |||
outValues[4] = log10(1.e6*d[2]); /* N2 density */ | |||
else | |||
outValues[4] = NAN; | |||
if (d[3] > 0.0) | |||
outValues[5] = log10(1.e6*d[3]); /* O2 density */ | |||
else | |||
outValues[5] = NAN; | |||
if (d[1] > 0.0) | |||
outValues[6] = log10(1.e6*d[1]); /* O density */ | |||
else | |||
outValues[6] = NAN; | |||
if (d[4] > 0.0) | |||
outValues[7] = log10(1.e6*d[4]); /* Ar density */ | |||
else | |||
outValues[7] = NAN; | |||
if (d[0] > 0.0) | |||
outValues[8] = log10(1.e6*d[0]); /* He density */ | |||
else | |||
outValues[8] = NAN; | |||
if (d[6] > 0.0) | |||
outValues[9] = log10(1.e6*d[6]); /* H density */ | |||
else | |||
outValues[9] = NAN; | |||
if (d[7] > 0.0) | |||
outValues[10] = log10(1.e6*d[7]); /* N - stable density */ | |||
else | |||
outValues[10] = NAN; | |||
/* simple error calculation */ | |||
for (i=0; i<13; i++) | |||
{ | |||
if (isnan(outValues[i])) | |||
outValues[i+13] = NAN; | |||
else if (i==0 || i==1 || i==12) | |||
outValues[i+13] = outValues[i]*errPercent; /* linear parm */ | |||
else | |||
outValues[i+13] = log10( pow(10.0,outValues[i]) * errPercent ); /* log parm */ | |||
} | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* madRunTsygan runs underlying Tsyganenko method getTsyganenkoField | |||
* | |||
* arguments: | |||
* 1. Python double representing mid_time in seconds since 1950-01-01 | |||
* 2. Python double representing GDLAT in degrees | |||
* 3. Python double representing GLON in degrees | |||
* 4. Python double representing GDALT in km | |||
* 5. Numpy double array of length 24 representing last 24 hours of SWSPD in m/s (last is present value) | |||
* 6. Python double representing imf in y gsm direction in nTesla at mid_time | |||
* 7. Numpy double array of length 24 representing last 24 hours of z gsm values in nT (last is present value) | |||
* 8. Numpy double array of length 24 representing last 24 hours of SWDEN in m^-3 (last is present value) | |||
* 9. Python double representing dst in nTesla at mid_time | |||
* 10. Python double 1D numpy vector representing the 4 outputs: | |||
* "TSYG_EQ_XGSM","TSYG_EQ_YGSM","TSYG_EQ_XGSE","TSYG_EQ_YGSE" | |||
* | |||
* returns: | |||
* Python int 0 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_madRunTsygan(PyObject * self, PyObject * args) | |||
{ | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
double * swspdValues; | |||
double * zgsmValues; | |||
double * swdenValues; | |||
double * outValues; | |||
/* input variables */ | |||
double mid_time; | |||
double gdlat, glon, gdalt; | |||
double dst, ygsm; | |||
PyArrayObject *swspdArr, *zgsmArr; | |||
PyArrayObject *swdenArr, *outArr; | |||
if (!PyArg_ParseTuple(args, "ddddO!dO!O!dO!", | |||
&mid_time, | |||
&gdlat, &glon, &gdalt, | |||
&PyArray_Type, &swspdArr, | |||
&ygsm, | |||
&PyArray_Type, &zgsmArr, | |||
&PyArray_Type, &swdenArr, | |||
&dst, | |||
&PyArray_Type, &outArr)) | |||
return NULL; | |||
/* check that one-D arrays */ | |||
if (swspdArr->nd != 1 || zgsmArr->nd != 1 || swdenArr->nd != 1 || outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of all arrays must be 1, not %i, %i %i, and %i", | |||
swspdArr->nd, zgsmArr->nd, swdenArr->nd, outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* force glon to between -180 and 180 */ | |||
while (glon < -180.0) glon += 360.0; | |||
while (glon > +180.0) glon -= 360.0; | |||
swspdValues = (double *)swspdArr->data; | |||
zgsmValues = (double *)zgsmArr->data; | |||
swdenValues = (double *)swdenArr->data; | |||
outValues = (double *)outArr->data; | |||
getTsyganenkoField(mid_time, | |||
gdlat, glon, gdalt, | |||
swspdValues, ygsm, | |||
zgsmValues, swdenValues, | |||
dst, | |||
outValues, | |||
outValues + 1, | |||
outValues + 2, | |||
outValues + 3); | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* traceTsyganenkoField returns the endpoint of a magnetic line using Tsyganenko model | |||
* | |||
* Traces to conjugate point, intersection with a given altitude in the | |||
* northern or southern hemisphere, to the apex, or to GSM XY plane | |||
* Uses TsyganenkoF fields. | |||
* Input arguments are either GSM or Geodetic, depending on inputType argument. | |||
* Output arguments are either GSM or Geodetic, depending on outputType | |||
* argument. | |||
* | |||
* arguments: | |||
* Python int - year | |||
* Python int - month | |||
* Python int - day | |||
* Python int - hour | |||
* Python int - min | |||
* Python int - sec | |||
* Python int - inputType (0 for geodetic, 1 for GSM) | |||
* Python int - outputType (0 for geodetic, 1 for GSM) | |||
* (the following parameter depend on inputType) | |||
* Python double in1 - geodetic altitude or ZGSM of starting point | |||
* Python double in2 - geodetic latitude or XGSM of starting point | |||
* Python double in3 - longitude or YGSM of starting point | |||
* Numpy double array of swspd_array - array of last 24 hourly solar wind speeds in m/s | |||
* Python double imf_ygsm_now - imf in y gsm direction in nTesla now | |||
* Numpy double array of imf_zgsm_array - array of last 24 hourly imf in z gsm direction in nTesla measured now | |||
* Numpy double array of swden_array - array of last 24 hourly solar wind density in m^-3 | |||
* Python double dst - dst in nTesla | |||
* Python int qualifier - 0 for conjugate, 1 for north_alt, 2 for south_alt, 3 for apex, 4 for GSM XY plane | |||
* Python double stopAlt - altitude to stop trace at, if qualifier is north_alt or south_alt. | |||
* If other qualifier, this parameter is ignored | |||
* Python double 1D numpy vector representing the 3 outputs, whose meaning depends on outputType | |||
* end_1 - geodetic altitude or ZGSM of starting point | |||
* end_2 - geodetic latitude or XGSM of starting point | |||
* end_3 - longitude or YGSM of starting point | |||
* | |||
* If trace fails, all three return values will be set to nan | |||
* | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_traceTsyganenkoField(PyObject * self, PyObject * args) | |||
{ | |||
/* python inputs */ | |||
int year = 0; | |||
int month = 0; | |||
int day = 0; | |||
int hour = 0; | |||
int min = 0; | |||
int sec = 0; | |||
int inputType = 0; | |||
int outputType = 0; | |||
double in1 = 0.0; | |||
double in2 = 0.0; | |||
double in3 = 0.0; | |||
PyArrayObject *swspdArr, *zgsmArr; | |||
double imf_ygsm_now; | |||
PyArrayObject *swdenArr, *outArr; | |||
double dst; | |||
int qualifier = 0; | |||
double stopAlt = 0.0; | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
int dayOfYear = 0; | |||
int imod = 0; /* flag to set conversion direction */ | |||
int result = 0, i=0; | |||
double * swspdValues; | |||
double * zgsmValues; | |||
double * swdenValues; | |||
double * outValues; | |||
double mid_time; | |||
double out1, out2, out3; | |||
/* inputs required for the latest Tsygenenko code - hard coded solar wind speeds */ | |||
double vx = 0.0; | |||
double vy = 29.8; | |||
double vz = 0.0; | |||
/* temporary variable for holding position during conversion */ | |||
double dim1 = 0.0; | |||
double dim2 = 0.0; | |||
double dim3 = 0.0; | |||
// get arguments | |||
if (!PyArg_ParseTuple(args, "iiiiiiiidddO!dO!O!didO!", | |||
&year, &month, &day, &hour, &min, &sec, | |||
&inputType, &outputType, | |||
&in1, &in2, &in3, | |||
&PyArray_Type, &swspdArr, | |||
&imf_ygsm_now, | |||
&PyArray_Type, &zgsmArr, | |||
&PyArray_Type, &swdenArr, | |||
&dst, | |||
&qualifier, &stopAlt, | |||
&PyArray_Type, &outArr)) | |||
{ | |||
return NULL; | |||
} | |||
/* check that one-D arrays */ | |||
if (swspdArr->nd != 1 || zgsmArr->nd != 1 || swdenArr->nd != 1 || outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of all arrays must be 1, not %i, %i %i, and %i", | |||
swspdArr->nd, zgsmArr->nd, swdenArr->nd, outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* force glon to between -180 and 180 if geodetic */ | |||
if (inputType == 0) | |||
{ | |||
while (in3 < -180.0) in3 += 360.0; | |||
while (in3 > +180.0) in3 -= 360.0; | |||
} | |||
swspdValues = (double *)swspdArr->data; | |||
zgsmValues = (double *)zgsmArr->data; | |||
swdenValues = (double *)swdenArr->data; | |||
outValues = (double *)outArr->data; | |||
/* convert time to seconds since 1/1/1950 */ | |||
mid_time = getKey(year, month, day, hour, min, sec); | |||
/* get day of year as required by Tsyganenko */ | |||
dayOfYear = madGetDayno(year, month, day); | |||
/* set Tsyganenko common block data to the right time - not thread safe!!!! */ | |||
TS_RECALC_F77(&year, &dayOfYear, &hour, &min, &sec, &vx, &vy, &vz); | |||
/* convert in* to geodetic if needed */ | |||
if (inputType == 1) | |||
{ | |||
imod = -1; /* converst gsm to cartesian */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &in2, &in3, &in1, &imod); | |||
/* now dim1 XGEO, dim2 YGEO, dim3 ZGEO */ | |||
/* convert cartesian to spherical */ | |||
TS_SPHCAR_F77(&in1, &in2, &in3, &dim1, &dim2, &dim3, &imod); | |||
/* now in1 R, in2 theta, in3 phi */ | |||
/* convert spherical to geocentric */ | |||
dim1 = in1*6371.2; | |||
dim2 = 90.0 - (in2/0.01745329); | |||
dim3 = in3/0.01745329; | |||
imod = 2; /* convert geocentric coordinates to geodetic */ | |||
in3 = dim3; | |||
CONVRT_F77(&imod, &in2, &in1, &dim2, &dim1); | |||
/* now in1 is gdalt, in2 is gdlat, in3 is glon */ | |||
} | |||
result = traceTsyganenkoField(mid_time, | |||
in2, | |||
in3, | |||
in1, | |||
swspdValues, | |||
imf_ygsm_now, | |||
zgsmValues, | |||
swdenValues, | |||
dst, | |||
qualifier, | |||
stopAlt, | |||
&out2, | |||
&out3, | |||
&out1); | |||
/* out1 gdalt or ZGSM, out2 gdlat or XGSM, out3 glon or YGSM */ | |||
/* convert to GSM if result okay and outputType == 1 and qualifier != 4 (since then already GSM output) */ | |||
if (outputType == 1 && result == 0 && qualifier != 4) | |||
{ | |||
imod = 1; | |||
/* get geocentric coordinates from geodetic */ | |||
CONVRT_F77(&imod, &out2, &out1, &dim2, &dim1); | |||
dim3 = out3; | |||
/* dim2 now gclat, dim1 = rkm, dim3 = glon */ | |||
/* convert from geocentric to spherical */ | |||
out2 = (90.0 - dim2)*0.01745329; | |||
out3 = dim3*0.01745329; | |||
out1 = dim1/6371.2; | |||
/* now out1 = R, out2 = theta, out3 = phi */ | |||
/* get cartesian coordinates from spherical */ | |||
TS_SPHCAR_F77(&out1, &out2, &out3, &dim1, &dim2, &dim3, &imod); | |||
/* now dim1-XGEO, dim2=YGEO, dim3=ZGEO */ | |||
/* convert cartesian into solar magnetospheric ones */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &out2, &out3, &out1, &imod); | |||
/* now out1 = ZGSM, out2=XGSM, out3 = YGSM */ | |||
} | |||
/* convert to Geodetic if result okay and outputType == 0 and qualifier == 4 (since then GSM output) */ | |||
if (outputType == 0 && result == 0 && qualifier == 4) | |||
{ | |||
imod = -1; /* converst gsm to cartesian */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &out2, &out3, &out1, &imod); | |||
/* now dim1 XGEO, dim2 YGEO, dim3 ZGEO */ | |||
/* convert cartesian to spherical */ | |||
TS_SPHCAR_F77(&out1, &out2, &out3, &dim1, &dim2, &dim3, &imod); | |||
/* now out1 R, out2 theta, out3 phi */ | |||
/* convert spherical to geocentric */ | |||
dim1 = out1*6371.2; | |||
dim2 = 90.0 - (out2/0.01745329); | |||
dim3 = out3/0.01745329; | |||
imod = 2; /* convert geocentric coordinates to geodetic */ | |||
out3 = dim3; | |||
CONVRT_F77(&imod, &out2, &out1, &dim2, &dim1); | |||
/* now out1 is gdalt, out2 is gdlat, out3 is glon */ | |||
} | |||
/* force out3 to between -180 and 180 if geodetic */ | |||
if (outputType == 0) | |||
{ | |||
while (out3 < -180.0) out3 += 360.0; | |||
while (out3 > +180.0) out3 -= 360.0; | |||
} | |||
if (result != 0) | |||
for (i=0; i<3; i++) | |||
outValues[i] = NAN; | |||
else | |||
{ | |||
outValues[0] = out1; | |||
outValues[1] = out2; | |||
outValues[2] = out3; | |||
} | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* traceIGRFField returns the endpoint of a magnetic line using IGRF model via lintra | |||
* | |||
* Traces to conjugate point, intersection with a given altitude in the | |||
* northern or southern hemisphere, to the apex, or to GSM XY plane | |||
* Uses IGRF fields. | |||
* Input arguments are either GSM or Geodetic, depending on inputType argument. | |||
* Output arguments are either GSM or Geodetic, depending on outputType | |||
* argument. | |||
* | |||
* arguments: | |||
* Python int - year | |||
* Python int - inputType (0 for geodetic, 1 for GSM) | |||
* Python int - outputType (0 for geodetic, 1 for GSM) | |||
* (the following parameter depend on inputType) | |||
* Python double in1 - geodetic altitude or ZGSM of starting point | |||
* Python double in2 - geodetic latitude or XGSM of starting point | |||
* Python double in3 - longitude or YGSM of starting point | |||
* Python int qualifier - 0 for conjugate, 1 for north_alt, 2 for south_alt, 3 for apex | |||
* 4 for GSM XY plane - but not possible for lintra, so raises an error | |||
* Python double stopAlt - altitude to stop trace at, if qualifier is north_alt or south_alt. | |||
* If other qualifier, this parameter is ignored | |||
* Python double 1D numpy vector representing the 3 outputs, whose meaning depends on outputType | |||
* end_1 - geodetic altitude or ZGSM of starting point | |||
* end_2 - geodetic latitude or XGSM of starting point | |||
* end_3 - longitude or YGSM of starting point | |||
* | |||
* If trace fails, all three return values will be set to nan | |||
* | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_traceIGRFField(PyObject * self, PyObject * args) | |||
{ | |||
/* python inputs */ | |||
int year = 0; | |||
int inputType = 0; | |||
int outputType = 0; | |||
double in1 = 0.0; | |||
double in2 = 0.0; | |||
double in3 = 0.0; | |||
int qualifier = 0; | |||
double stopAlt = 0.0; | |||
PyArrayObject *outArr; | |||
/* local variables */ | |||
char errBuf[1000] = ""; | |||
int result = 0; | |||
double * outValues; | |||
double fyear; | |||
/* temporary variable for holding position during conversion */ | |||
double dim1 = 0.0; | |||
double dim2 = 0.0; | |||
double dim3 = 0.0; | |||
/* geocentric data */ | |||
double gclat = 0.0; | |||
double rkm = 0.0; | |||
double plat = 0.0; /* end point geocentric latitude (deg) */ | |||
double plon = 0.0; /* end point geocentric longitude (deg) */ | |||
double prkm = 0.0; /* end point radial distance (km) */ | |||
double arc = 0.0; /* arc length of field line traced (km) */ | |||
double arad = 0.0; /* apex radius of field line (earth radii) */ | |||
double alat = 0.0; /* apex latitude of field line (deg) */ | |||
double alon = 0.0; /* apex longitude of field line (deg) */ | |||
double end_gdlat; | |||
double end_glon; | |||
double end_gdalt; | |||
/* conversion direction */ | |||
int imod = 0; | |||
int ier = 0; /* error indicator */ | |||
int npr = 0; /* used by lintra */ | |||
int init = 2; /* used by lintra */ | |||
int istop = 0; /* sets direction of lintra trace */ | |||
// get arguments | |||
if (!PyArg_ParseTuple(args, "iiidddidO!", | |||
&year, | |||
&inputType, &outputType, | |||
&in1, &in2, &in3, | |||
&qualifier, &stopAlt, | |||
&PyArray_Type, &outArr)) | |||
{ | |||
return NULL; | |||
} | |||
if (qualifier == 4) | |||
{ | |||
sprintf(errBuf, "Qualifier for IGRF model cannot be 4"); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* check that one-D arrays */ | |||
if (outArr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of output array must be 1, not %i", | |||
outArr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* force glon/in3 to between -180 and 180 if geodetic */ | |||
if (inputType == 0) | |||
{ | |||
while (in3 < -180.0) in3 += 360.0; | |||
while (in3 > +180.0) in3 -= 360.0; | |||
} | |||
outValues = (double *)outArr->data; | |||
fyear = (double)year; | |||
/* convert in* to geodetic if needed */ | |||
if (inputType == 1) | |||
{ | |||
imod = -1; /* converst gsm to cartesian */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &in2, &in3, &in1, &imod); | |||
/* now dim1 XGEO, dim2 YGEO, dim3 ZGEO */ | |||
/* convert cartesian to spherical */ | |||
TS_SPHCAR_F77(&in1, &in2, &in3, &dim1, &dim2, &dim3, &imod); | |||
/* now in1 R, in2 theta, in3 phi */ | |||
/* convert spherical to geocentric */ | |||
dim1 = in1*6371.2; | |||
dim2 = 90.0 - (in2/0.01745329); | |||
dim3 = in3/0.01745329; | |||
imod = 2; /* convert geocentric coordinates to geodetic */ | |||
in3 = dim3; | |||
CONVRT_F77(&imod, &in2, &in1, &dim2, &dim1); | |||
/* now in1 is gdalt, in2 is gdlat, in3 is glon */ | |||
} | |||
imod = 1; /* convert geodetic to geocentric */ | |||
CONVRT_F77(&imod, &in2, &in1, &gclat, &rkm); | |||
switch (qualifier) | |||
{ | |||
case 0: /* conjugate - try the opposite hemisphere first, but its possible | |||
we'll need to try this hemisphere */ | |||
istop = 1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&in1, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success - more than 50 km traced, and nearly the same altitude returned */ | |||
if (arc > 50.0) | |||
{ | |||
if (fabs(end_gdalt - in1) < 50.0) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
} | |||
/* opposite hemisphere failed - try the same hemisphere */ | |||
istop = -1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&in1, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success - more than 50 km traced, and nearly the same altitude returned */ | |||
if (arc > 50.0) | |||
{ | |||
if (fabs(end_gdalt - in1) < 50.0) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
} | |||
/* both hemispheres failed */ | |||
end_gdalt = NAN; | |||
end_gdlat = NAN; | |||
end_glon = NAN; | |||
break; | |||
case 1: /* north_alt - randomly try opposite hemisphere first (50/50 guess)*/ | |||
istop = 1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&stopAlt, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success - more than 50 km traced, and north of starting point */ | |||
if (arc > 50.0) | |||
{ | |||
if (end_gdlat - in2 > 0.1) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
/* or we may have started below the intercept */ | |||
if (in1 < stopAlt && end_gdlat - in2 < -0.1) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
} | |||
/* opposite hemisphere failed - try the same hemisphere */ | |||
istop = -1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&stopAlt, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success */ | |||
if (ier == 0) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
/* both hemispheres failed */ | |||
end_gdalt = NAN; | |||
end_gdlat = NAN; | |||
end_glon = NAN; | |||
break; | |||
case 2: /* south_alt - randomly try opposite hemisphere first (50/50 guess)*/ | |||
istop = 1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&stopAlt, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success - more than 50 km traced, and south of starting point */ | |||
if (arc > 50.0) | |||
{ | |||
if (end_gdlat - in2 < -0.1) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
/* or we may have started below the intercept */ | |||
if (in1 < stopAlt && end_gdlat - in2 > 0.1) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
} | |||
/* opposite hemisphere failed - try the same hemisphere */ | |||
istop = -1; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&stopAlt, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert back to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
/* check for success - more than 50 km traced, and south of starting point */ | |||
if (ier == 0) | |||
{ | |||
end_glon = plon; | |||
break; | |||
} | |||
/* both hemispheres failed */ | |||
end_gdalt = NAN; | |||
end_gdlat = NAN; | |||
end_glon = NAN; | |||
break; | |||
case 3: /* apex - set istop = 0 */ | |||
istop = 0; | |||
LINTRA_F77(&fyear, | |||
&gclat, | |||
&in3, | |||
&rkm, | |||
&in1, | |||
&in1, | |||
&plat, | |||
&plon, | |||
&prkm, | |||
&arc, | |||
&arad, | |||
&alat, | |||
&alon, | |||
&istop, | |||
&npr, | |||
&init, | |||
&ier); | |||
/* convert apex to geodetic */ | |||
imod = 2; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &plat, &prkm); | |||
end_glon = plon; | |||
break; | |||
default: | |||
sprintf(errBuf, "qualifier must be 0-3, not %i", | |||
qualifier); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
/* end_gdalt gdalt or ZGSM, end_gdlat gdlat or XGSM, end_glon glon or YGSM */ | |||
/* convert to GSM if result okay and outputType == 1 and qualifier != 4 (since then already GSM output) */ | |||
if (outputType == 1 && result == 0 && qualifier != 4) | |||
{ | |||
imod = 1; | |||
/* get geocentric coordinates from geodetic */ | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &dim2, &dim1); | |||
dim3 = end_glon; | |||
/* dim2 now gclat, dim1 = rkm, dim3 = glon */ | |||
/* convert from geocentric to spherical */ | |||
end_gdlat = (90.0 - dim2)*0.01745329; | |||
end_glon = dim3*0.01745329; | |||
end_gdalt = dim1/6371.2; | |||
/* now end_gdalt = R, end_gdlat = theta, end_glon = phi */ | |||
/* get cartesian coordinates from spherical */ | |||
TS_SPHCAR_F77(&end_gdalt, &end_gdlat, &end_glon, &dim1, &dim2, &dim3, &imod); | |||
/* now dim1-XGEO, dim2=YGEO, dim3=ZGEO */ | |||
/* convert cartesian into solar magnetospheric ones */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &end_gdlat, &end_glon, &end_gdalt, &imod); | |||
/* now end_gdalt = ZGSM, end_gdlat=XGSM, end_glon = YGSM */ | |||
} | |||
/* convert to Geodetic if result okay and outputType == 0 and qualifier == 4 (since then GSM output) */ | |||
if (outputType == 0 && result == 0 && qualifier == 4) | |||
{ | |||
imod = -1; /* converst gsm to cartesian */ | |||
GEOGSM_F77(&dim1, &dim2, &dim3, &end_gdlat, &end_glon, &end_gdalt, &imod); | |||
/* now dim1 XGEO, dim2 YGEO, dim3 ZGEO */ | |||
/* convert cartesian to spherical */ | |||
TS_SPHCAR_F77(&end_gdalt, &end_gdlat, &end_glon, &dim1, &dim2, &dim3, &imod); | |||
/* now end_gdalt R, end_gdlat theta, end_glon phi */ | |||
/* convert spherical to geocentric */ | |||
dim1 = end_gdalt*6371.2; | |||
dim2 = 90.0 - (end_gdlat/0.01745329); | |||
dim3 = end_glon/0.01745329; | |||
imod = 2; /* convert geocentric coordinates to geodetic */ | |||
end_glon = dim3; | |||
CONVRT_F77(&imod, &end_gdlat, &end_gdalt, &dim2, &dim1); | |||
/* now end_gdalt is gdalt, end_gdlat is gdlat, end_glon is glon */ | |||
} | |||
outValues[0] = end_gdalt; | |||
outValues[1] = end_gdlat; | |||
outValues[2] = end_glon; | |||
/* success */ | |||
return(Py_BuildValue("i", 0)); | |||
} | |||
/*********************************************************************** | |||
* | |||
* faradayRotation calculates total phase shift due to a single pass of an | |||
* electromagnetic wave from a ground instrument to a point | |||
* in space. Also calculates the total tec along that pass, | |||
* plus the total tec is the path were extended to 22000 km. | |||
* | |||
* Uses quasi-longitudinal approximation, so not appropriate if perp | |||
* to magnetic field. | |||
* Uses coord and IRI from geolib | |||
* | |||
* Inputs: | |||
* Python ints - year month day hour min sec | |||
* Python floats - sgdlat slon sgdalt gdlat glon gdalt freq | |||
* Numpy int32 array of length 13 with 13 previous values of ap3 (last is present value) | |||
* Python double representing F107 in W/m2/Hz | |||
* | |||
* where sgdlat slon sgdalt are geodetic station location, | |||
* gdlat glon gdalt are geodetic point locations, and freq is | |||
* wave frequency in Hz | |||
* | |||
* Returns: a tuple with three items: | |||
* 1. one way faraday rotation in radians, NAN if error | |||
* 2. total tec from station to point in electrons/m^2 | |||
* 3. total tec from station to 22000 km along line through | |||
* point in electrons/m^2 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_faradayRotation(PyObject * self, PyObject * args) | |||
{ | |||
char errBuf[1000] = ""; | |||
int year = 0; | |||
int month = 0; | |||
int day = 0; | |||
int hour = 0; | |||
int min = 0; | |||
int sec = 0; | |||
double sgdlat = 0.0; | |||
double slon = 0.0; | |||
double sgdalt = 0.0; | |||
double gdlat = 0.0; | |||
double glon = 0.0; | |||
double gdalt = 0.0; | |||
double freq = 0.0; | |||
double tec = 0.0; | |||
double total_tec = 0.0; | |||
double result = 0; | |||
int * ap3Values; | |||
double f107; | |||
PyArrayObject *ap3Arr; | |||
PyObject * pyRotation; | |||
PyObject * pyTec; | |||
PyObject * pyTecTotal; | |||
// get arguments | |||
if (!PyArg_ParseTuple(args, "iiiiiidddddddO!d", | |||
&year, &month, &day, | |||
&hour, &min, &sec, | |||
&sgdlat, &slon, &sgdalt, | |||
&gdlat, &glon, &gdalt, | |||
&freq, | |||
&PyArray_Type, &ap3Arr, | |||
&f107)) | |||
{ | |||
return NULL; | |||
} | |||
/* check that one-D array */ | |||
if (ap3Arr->nd != 1) | |||
{ | |||
sprintf(errBuf, "Number of dimensions of ap3 array must be 1, not %i\\n", | |||
ap3Arr->nd); | |||
PyErr_SetString(PyExc_TypeError, errBuf); | |||
return NULL; | |||
} | |||
ap3Values = (int *)ap3Arr->data; | |||
result = faraday_rotation_3(year, month, day, hour, min, sec, | |||
sgdlat, slon, sgdalt, | |||
gdlat, glon, gdalt, ap3Values, f107, freq, | |||
&tec, &total_tec); | |||
// create min longitude | |||
pyRotation = PyFloat_FromDouble(result); | |||
pyTec = PyFloat_FromDouble(tec); | |||
pyTecTotal = PyFloat_FromDouble(total_tec); | |||
//return all info; | |||
return Py_BuildValue("(OOO)", pyRotation, pyTec, pyTecTotal); | |||
} | |||
/*********************************************************************** | |||
* | |||
* radarToGeodetic returns gdalt,gdlat,glon given radar location and az,el,range | |||
* | |||
* | |||
* arguments: | |||
* 1. radar geodetic latitude | |||
* 2. radar longitude | |||
* 3. radar geodetic altitude | |||
* 4. azimuth | |||
* 5. elevation | |||
* 6. range in km | |||
* | |||
* returns: | |||
* 1. Python tuple with (gdlat,glon,gdalt) of point | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_radarToGeodetic(PyObject * self, PyObject * args) | |||
{ | |||
PyObject *retObj; | |||
/* input arguments */ | |||
double slatgd = 0.0; | |||
double slon = 0.0; | |||
double saltgd = 0.0; | |||
double az = 0.0; | |||
double el = 0.0; | |||
double range = 0.0; | |||
/* return values */ | |||
double gdalt = 0.0; | |||
double gdlat = 0.0; | |||
double glon = 0.0; | |||
/* arguments needed by point */ | |||
double sr = 0.0; | |||
double slatgc = 0.0; | |||
int direction = 1; | |||
double pr = 0.0; | |||
double gclat = 0.0; | |||
// get input arguments | |||
if (!PyArg_ParseTuple(args, "dddddd", | |||
&slatgd, | |||
&slon, | |||
&saltgd, | |||
&az, | |||
&el, | |||
&range)) | |||
{ | |||
return NULL; | |||
} | |||
if (range < 0.0) | |||
{ | |||
PyErr_SetString(PyExc_TypeError, "Range must be > 0 for radarToGeodetic"); | |||
return NULL; | |||
} | |||
/* force slon to between -180 and 180 */ | |||
while (slon < -180.0) slon += 360.0; | |||
while (slon > +180.0) slon -= 360.0; | |||
/* get geocentric coordinates needed by point */ | |||
CONVRT_F77(&direction,&slatgd,&saltgd,&slatgc,&sr); | |||
POINT_F77(&sr,&slatgc,&slon,&az,&el,&range, | |||
&pr,&gclat,&glon); | |||
direction = 2; | |||
CONVRT_F77(&direction,&gdlat,&gdalt,&gclat,&pr); | |||
/* success */ | |||
retObj = Py_BuildValue("(ddd)", gdlat,glon,gdalt); | |||
return(retObj); | |||
} | |||
/*********************************************************************** | |||
* | |||
* geodeticToRadar given gdalt,gdlat,glon and radar location, returns az,el,range | |||
* | |||
* | |||
* arguments: | |||
* 1. radar geodetic latitude | |||
* 2. radar longitude | |||
* 3. radar geodetic altitude | |||
* 4. point geodetic latitude | |||
* 5. point longitude | |||
* 6. point geodetic altitude | |||
* | |||
* returns: | |||
* 1. Python tuple with (az,el,range) of radar view of point | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_geodeticToRadar(PyObject * self, PyObject * args) | |||
{ | |||
PyObject *retObj; | |||
/* input arguments */ | |||
double slatgd = 0.0; | |||
double slon = 0.0; | |||
double saltgd = 0.0; | |||
double gdalt = 0.0; | |||
double gdlat = 0.0; | |||
double glon = 0.0; | |||
/* return values */ | |||
double az = 0.0; | |||
double el = 0.0; | |||
double range = 0.0; | |||
/* arguments needed by look */ | |||
double sr = 0.0; | |||
double slatgc = 0.0; | |||
double pr = 0.0; | |||
double gclat = 0.0; | |||
int direction = 1; | |||
// get input arguments | |||
if (!PyArg_ParseTuple(args, "dddddd", | |||
&slatgd, | |||
&slon, | |||
&saltgd, | |||
&gdlat, | |||
&glon, | |||
&gdalt)) | |||
{ | |||
return NULL; | |||
} | |||
/* force slon to between -180 and 180 */ | |||
while (slon < -180.0) slon += 360.0; | |||
while (slon > +180.0) slon -= 360.0; | |||
/* force glon to between -180 and 180 */ | |||
while (glon < -180.0) glon += 360.0; | |||
while (glon > +180.0) glon -= 360.0; | |||
/* get geocentric coordinates needed by look */ | |||
CONVRT_F77(&direction,&slatgd,&saltgd,&slatgc,&sr); | |||
CONVRT_F77(&direction,&gdlat,&gdalt,&gclat,&pr); | |||
LOOK_F77(&sr,&slatgc,&slon,&pr,&gclat,&glon,&az,&el,&range); | |||
/* force az to between -180 and 180 */ | |||
while (az < -180.0) az += 360.0; | |||
while (az > +180.0) az -= 360.0; | |||
/* success */ | |||
retObj = Py_BuildValue("(ddd)", az,el,range); | |||
return(retObj); | |||
} | |||
/*********************************************************************** | |||
* | |||
* getUtFromDate returns the number of seconds as a float since 1/1/1950 | |||
* | |||
* arguments: | |||
* Year as Python int | |||
* Month as Python int | |||
* Day as Python int | |||
* Hour as Python int | |||
* Min as Python int | |||
* Sec as Python int | |||
* Centisec as Python int | |||
* | |||
* returns: | |||
* Python double giving the number of seconds as a float since 1/1/1950 | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_getUtFromDate(PyObject * self, PyObject * args) | |||
{ | |||
int year = 0; | |||
int month = 0; | |||
int day = 0; | |||
int hour = 0; | |||
int min = 0; | |||
int sec = 0; | |||
int centisec = 0; | |||
double ut = 0.0; | |||
PyObject *retObj; | |||
// get date arguments | |||
if (!PyArg_ParseTuple(args, "iiiiiii", &year, &month, &day, &hour, &min, &sec, ¢isec)) | |||
{ | |||
return NULL; | |||
} | |||
ut = getKey(year, month, day, hour, min, sec); | |||
/* add centiseconds */ | |||
ut += centisec/100.0; | |||
retObj = Py_BuildValue("d", ut); | |||
return(retObj); | |||
} | |||
/*********************************************************************** | |||
* | |||
* getDateFromUt returns a date as a list of integers given ut as seconds since 1/1/1950 | |||
* | |||
* arguments: | |||
* Python double giving the number of seconds as a float since 1/1/1950 | |||
* | |||
* returns: list containing | |||
* Year as Python int | |||
* Month as Python int | |||
* Day as Python int | |||
* Hour as Python int | |||
* Min as Python int | |||
* Sec as Python int | |||
* Centisec as Python int | |||
* Day of year as Python int | |||
* | |||
* throws: | |||
* PyExc_TypeError if illegal argument passed | |||
* | |||
*/ | |||
static PyObject * _derive_getDateFromUt(PyObject * self, PyObject * args) | |||
{ | |||
int year = 0; | |||
int month = 0; | |||
int day = 0; | |||
int imd = 0; | |||
int hour = 0; | |||
int min = 0; | |||
int ihm = 0; | |||
int sec = 0; | |||
int centisec = 0; | |||
int ics = 0; | |||
int dayOfYear = 0; | |||
double ut = 0.0; | |||
PyObject * retDateList; | |||
PyObject * pyTempValue; /* python object to add to lists, then release */ | |||
// get ut | |||
if (!PyArg_ParseTuple(args, "d", &ut)) | |||
{ | |||
return NULL; | |||
} | |||
dinvmadptr(ut, &year, &imd, &ihm, &ics); | |||
month = imd/100; | |||
day = imd - month*100; | |||
hour = ihm/100; | |||
min = ihm - hour*100; | |||
sec = ics/100; | |||
centisec = ics - sec*100; | |||
dayOfYear = madGetDayno(year, month, day); | |||
retDateList = PyList_New(0); | |||
pyTempValue = PyInt_FromLong(year); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(month); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(day); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(hour); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(min); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(sec); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(centisec); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
pyTempValue = PyInt_FromLong(dayOfYear); | |||
PyList_Append(retDateList, pyTempValue); | |||
Py_DECREF(pyTempValue); | |||
return(retDateList); | |||
} | |||
/********** Initialization code for module ******************************/ | |||
static PyMethodDef _deriveMethods[] = | |||
{ | |||
{"madDispatch", _derive_madDispatch, METH_VARARGS, "Call a derivation method"}, | |||
{"madIsrim", _derive_madIsrim, METH_VARARGS, "Call Shunrongs model"}, | |||
{"madRunIri", _derive_madRunIri, METH_VARARGS, "Call IRI"}, | |||
{"madRunMsis", _derive_madRunMsis, METH_VARARGS, "Call MSIS"}, | |||
{"madRunTsygan", _derive_madRunTsygan, METH_VARARGS, "Call Tsyganenko"}, | |||
{"traceTsyganenkoField", _derive_traceTsyganenkoField, METH_VARARGS, "Trace Tsyganenko field"}, | |||
{"traceIGRFField", _derive_traceIGRFField, METH_VARARGS, "Trace IGRF field"}, | |||
{"faradayRotation", _derive_faradayRotation, METH_VARARGS, "Get Faraday Rotation"}, | |||
{"radarToGeodetic", _derive_radarToGeodetic, METH_VARARGS, "Convert radar coords to geodetic"}, | |||
{"geodeticToRadar", _derive_geodeticToRadar, METH_VARARGS, "Convert geodetic to radar coords"}, | |||
{"getUtFromDate", _derive_getUtFromDate, METH_VARARGS, "get secs from 1950 UT from date"}, | |||
{"getDateFromUt", _derive_getDateFromUt, METH_VARARGS, "get date as secs from 1950 UT"}, | |||
{NULL, NULL, 0, NULL} /* Sentinel */ | |||
}; | |||
void init_derive(void) | |||
{ | |||
PyImport_AddModule("_derive"); | |||
Py_InitModule("_derive", _deriveMethods); | |||
import_array(); | |||
} | |||
""" | |||
###### main code starts here ########## | |||
f = open('../../madrigal/_derive_new.c', 'w') | |||
f.write(startTemplate) | |||
madMethods = madrigal.derivation.MadrigalDerivedMethods | |||
methodNames = list(madMethods.keys()) | |||
for methodName in methodNames: | |||
if methodName == methodNames[1]: # first method is python - so skip it | |||
f.write(codeTemplate1 % (methodName, methodName)) | |||
else: | |||
items = madMethods[methodName] | |||
if len(items) >= 3: | |||
if items[2] == 'python': | |||
continue | |||
f.write(codeTemplate2 % (methodName, methodName)) | |||
f.write(endTemplate) | |||
f.close() |