GIStemp STEP1_EX_station

Overview

We will be looking at the EXTENSIONS stationstring C library in this section. I’ve chosen to leave the program listings as one large block rather than interleave comments. This is so that experienced programmers can just “see the code” and read it. Some of the lines exceed the width of the panel. I’ll be fixing this in a later pass.

This is a very long library of functions complete with an extensive “make file” to install it into a production computer system. It’s well made and production quality; but don’t expect to just read it unless you have some decent programming experience. I don’t put much commentary in this page, it’s mostly just to let the curious see what’s in the library.

This program is complied by and run by “make” files in STEP1/EXTENSIONS/stationstring directory. The are identical to the ones in the “monthlydata” directory. Just remember that, though identical, this is a second copy in a second directory.

The Make Files

There are three of these, named “make_clean”, “make_shared”, and “Makefile.pre.in”. There is also a file named “Setup.in” in the same directory.

Here are The Make Files should you wish to see them.

The C program stationstringmodule.c

This program looks to me like a very well written set of stock library functions. I’m not going to put time into documenting them until the very end. It’s the broken bits I care about finding the most, not the well designed parts. The “good bits” you just compile and run.

My only complaint would be that there isn’t a companion document to tell you what each function is and what it is used for. But there seems to be no real documentaton with GIStemp at all.

Here is the listing. The explanation will follow some months or weeks from now below, after the === bar.

   
 
/*****************************************************************************
 * Copyright (C) 1998, Jay Glascoe, SSAI, NASA/GISS
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *****************************************************************************/

/*****************************************************************************
 * file "stationstringmodule.c", by Jay Glascoe, 1998
 *****************************************************************************/

/* StationString objects */

#include 
#include 
#include "Python.h"

#define FEQUALS(x, y) (fabs(x - (y)) ob_type == &StationString_Type)

static StationStringObject *
newStationStringObject(char *sp, int length)
{
    StationStringObject *self;
    char *sp2;
    self = PyObject_NEW(StationStringObject, &StationString_Type);
    if (self == NULL)
	return NULL;

    self->dict_sp = (char *)PyMem_Malloc((length + 1) * sizeof(char));
    if (self->dict_sp == NULL)
	return NULL;
    
    strcpy(self->dict_sp, sp);
    
    for (sp2 = self->dict_sp; *sp2 != '\n'; ++sp2)
	/* nada */;
    
    *sp2 = '';
    self->data_sp = ++sp2;
    self->dict = NULL;
    self->data = NULL;
    self->years = 0;

/*      printf("string: '%s'\ndict: '%s'\ndata: '%s'\nlength: %d\n", */
/*  	   sp, self->dict_sp, self->data_sp, length); */

    return self;
}

/* StationString methods */

static PyObject *
StationString_dict(StationStringObject *self, PyObject *args)
{
    char *sp = self->dict_sp;
    char *kp;
    char *vp;
    PyObject *pydict;
    if (!PyArg_ParseTuple(args, ""))
	return NULL;
    if (self->dict != NULL)
    {
	Py_INCREF(self->dict);
	return self->dict;
    }
    pydict = PyDict_New();
    if (pydict == NULL)
	return NULL;
    for (kp = strtok(sp, "\t"), vp = strtok(NULL, "\t");
	 vp != NULL;
	 kp = strtok(NULL, "\t"), vp = strtok(NULL, "\t"))
    {
	PyObject *pyval;
	if (strcmp(kp, "begin") == 0 || strcmp(kp, "ipop") == 0)
	    pyval = PyInt_FromLong((long)atoi(vp));
	else if (strcmp(kp, "lat") == 0 || strcmp(kp, "lon") == 0)
	    pyval = PyFloat_FromDouble((double)atof(vp));
	else
	    pyval = PyString_FromString(vp);
	PyDict_SetItemString(pydict, kp, pyval);
	Py_DECREF(pyval);
    }
    self->dict = pydict;
    Py_INCREF(pydict);
    return pydict;
}

static PyObject *
StationString_years(StationStringObject *self, PyObject *args)
{
    char *sp = self->data_sp;
    char *dp;
    long length;
    if (self->years != 0)
	return PyInt_FromLong(self->years);
    for (length = 1, dp = sp; *dp != 0; ++dp, *dp == ' ' ? ++length : 0)
	/* nada */ ;
    self->years = length / 12;
    return PyInt_FromLong(self->years);
}

static PyObject *
StationString_data_years(StationStringObject *self, PyObject *args)
{
    char *sp = self->data_sp;
    char *dp;
    int m;
    PyObject *pydata;
    PyObject *pyyears;
    PyObject *ret;
    if (self->data != NULL)
    {
	if (self->years == 0)
	    return NULL;
	ret = Py_BuildValue("Ol", self->data, self->years);
	return ret;
    }
    pydata = PyList_New(12);
    if (pydata == NULL)
	return NULL;
    if (self->years == 0)
    {
	pyyears = StationString_years(self, args);
	Py_DECREF(pyyears);
    }
    dp = (char *)PyMem_Malloc(20 * sizeof(char));
    for (m = 0, dp = strtok(sp, " "); m years);
	if (monthly == NULL)
	    return NULL;
	for (n = 0; n years; ++n, dp = strtok(NULL, " "))
	{
	    double datum = atoi(dp) * 0.1;
	    PyList_SetItem(monthly, n, PyFloat_FromDouble(datum));
	}
	PyList_SetItem(pydata, m, monthly);
    }
    self->data = pydata;
    return Py_BuildValue("Ol", pydata, self->years);
}

static PyObject *
StationString_data(StationStringObject *self, PyObject *args)
{
    PyObject *tuple;
    PyObject *retval;
    if (self->data != NULL)
    {
	Py_INCREF(self->data);
	return self->data;
    }
    tuple = StationString_data_years(self, args);
    retval = PyTuple_GetItem(tuple, 0);
    Py_INCREF(retval);
    Py_DECREF(tuple);
    return retval;
}

static void
StationString_dealloc(StationStringObject *self)
{
    PyMem_Free(self->dict_sp);
    /* PyMem_Free(self->data_sp); */
    Py_XDECREF(self->dict);
    Py_XDECREF(self->data);
    /* PyMem_DEL(self); */
}

static PyObject *
StationString_to_text(StationStringObject *self, PyObject *args)
{
    int maxlen = 8192;
    char *text = PyMem_Malloc(maxlen * sizeof(char));
    char *id;
    PyObject *py_empty_tuple = PyTuple_New(0);
    PyObject *pydict = StationString_dict(self, py_empty_tuple);
    PyObject *pydata = StationString_data(self, py_empty_tuple);
    PyObject *pytext;
    int offset = 0;
    int begin = (int)PyInt_AsLong(PyDict_GetItemString(pydict, "begin"));
    Py_DECREF(py_empty_tuple);
    if (!PyArg_ParseTuple(args, "s", &id))
	return NULL;
    {
	char format[] = " %4ld%5ld%s%4s%-36s";
	double lat = PyFloat_AsDouble(PyDict_GetItemString(pydict, "lat"));
	double lon = PyFloat_AsDouble(PyDict_GetItemString(pydict, "lon"));
	char *ht = PyString_AsString(PyDict_GetItemString(pydict, "elevs"));
	char *name =PyString_AsString(PyDict_GetItemString(pydict, "name"));
	long ilat = (long)floor(10.0 * lat + 0.5);
	long ilon = (long)floor(10.0 * lon + 0.5);
	offset += sprintf(text, format, ilat, ilon, id, ht, name);
	text[offset] = '\n';
	++offset;
    }
    {
	int n;
	for (n = 0; n years; ++n)
	{
	    int year = n + begin;
	    int m;
	    offset += sprintf(text + offset, "%4d", year);
	    for (m = 0; m  maxlen / 2)
	    {
		maxlen *= 2;
		text = (char *)PyMem_Realloc(text, maxlen * sizeof(char));
	    }
	}
	text[offset] = 0;
    }
    pytext = PyString_FromStringAndSize(text, offset);
    Py_DECREF(pydict);
    Py_DECREF(pydata);
    PyMem_Free((void *)text);
    return pytext;
}

static PyObject *
StationString_combine(StationStringObject *self, PyObject *args)
{
    PyObject *pydict = NULL;
    PyObject *pyothers;

    long new_years, new_begin, new_end, length;
    double BAD;

    int j, i, n, m;
    int me_pos  ; /* Py_ssize_t me_pos; */
    PyObject *me_key, *me_value;

    double *new_sums[12];
    long *new_wgts[12];
    
    PyObject *pynew_data;
    PyObject *pynew_dict;
    PyObject *retval;
    PyObject *empty_args = PyTuple_New(0);
    
    if (!PyArg_ParseTuple(args, "Od", &pyothers, &BAD))
	return NULL;

    new_begin = 9999;
    new_end = -9999;

    PyList_Append(pyothers, (PyObject *)self);

    length = PyList_Size(pyothers);
    for (n = 0; n years;
	long other_begin = \
	    PyInt_AsLong(PyDict_GetItemString(other_pydict, "begin"));
	long other_end = other_begin + other_years - 1;
	if (other_begin  new_end)
	    new_end = other_end;
	Py_DECREF(other_pydict);
	Py_DECREF(other_pydata);
/*  	printf("okay1\n"); */
	fflush(stdout);
    }
    new_years = new_end - new_begin + 1;

/*  	printf("okay2 %d %d %d\n", new_years, new_begin, new_end); */
    for (m = 0; m < 12; ++m)
    {
	int n;
	
	new_sums[m] = (double *)PyMem_Malloc(new_years * sizeof(double));
	new_wgts[m] = (long *)PyMem_Malloc(new_years * sizeof(long));
	
	for (n = 0; n < new_years; ++n)
	    new_sums[m][n] = new_wgts[m][n] = 0;
    }
    	
    for (i = 0; i dict;
	PyObject *other_pydata = other->data;
	long other_years = other->years;
	long other_begin = \
	    PyInt_AsLong(PyDict_GetItemString(other_pydict, "begin"));
	int m;
	for (m = 0; m < 12; ++m)
	{
	    PyObject *other_monthly = PyList_GetItem(other_pydata, m);
	    int n;
	    for (n = 0; n < other_years; ++n)
	    {
		long year;
		double datum = \
		    PyFloat_AsDouble(PyList_GetItem(other_monthly, n));
/*  		printf("okay3 %f\n", datum); */
		fflush(stdout);
		if (FEQUALS(datum, BAD))
		    continue;
		year = n + other_begin;
		new_sums[m][year - new_begin] += datum;
		new_wgts[m][year - new_begin] += 1;
	    }
	}
    }

    pynew_data = PyList_New(12);
    
    for (m = 0; m < 12; ++m)
    {
	PyObject *pynew_monthly = PyList_New(new_years);
	for (n = 0; n dict;
    
    while ( (j = PyDict_Next(pydict, &me_pos, &me_key, &me_value)) )
    {
	Py_INCREF(me_key);
	Py_INCREF(me_value);
	PyDict_SetItem(pynew_dict, me_key, me_value);
    }
    PyDict_SetItemString(pynew_dict, "begin", PyInt_FromLong(new_begin));

    for (m = 0; m  maxlen / 2)
	{
	    maxlen *= 2;
	    string = (char *)PyMem_Realloc(string, maxlen * sizeof(char));
	}
    }
    PyDict_Next(pydict, &j, &pkey, &pvalue);
    
    string[len - 1] = '\n';
    
    for (m = 0; m < 12; ++m)
    {
	
	PyObject *monthly = PyList_GetItem(pydata, m);
	int years = PyList_Size(monthly);
	int n;
	for (n = 0; n  maxlen / 2)
	    {
		maxlen *= 2;
		string = (char *)PyMem_Realloc(string, maxlen * sizeof(char));
	    }
	}
    }
    string[len - 1] = '';
    
    pystring = PyString_FromString(string);
    PyMem_Free(string);
    
    return pystring;
}

static PyObject *
stationstring_from_lines(PyObject *self, PyObject *args)
{
    long begin, end, size, years, IBAD = 9999L;
    PyObject *pylines, *pydata, *retval;
    int i;
    
    if (!PyArg_ParseTuple(args, "O", &pylines))
	return NULL;

    begin = 9999;
    end = 0;

    size = PyList_Size(pylines);
    for (i = 0; i < size; ++i) 
    {
	PyObject *pyline = PyList_GetItem(pylines, i);
	char *line = PyString_AsString(pyline);
	char id[20];
	int year;
	if (!sscanf(line, "%12s%4d", id, &year))
	    return NULL;
	if (year  end)
	    end = year;
    }
    years = end - begin + 1;
    {
	long **data = (long **)PyMem_Malloc(years * sizeof(long *));
	int m, n;
	pydata = PyList_New(12);
	for (n = 0; n < years; ++n)
	{
	    data[n] = (long *)PyMem_Malloc(12 * sizeof(long));
	    for (m = 0; m < 12; ++m)
		data[n][m] = IBAD;
	}
	for (i = 0; i < size; ++i)
	{
	    PyObject *pyline = PyList_GetItem(pylines, i);
	    char *line = PyString_AsString(pyline);
	    int index, offset, year;
	    if (!sscanf(line + 12, "%4d", &year))
		return NULL;
	    index = year - begin;
	    for (m = 0, offset = 16; m < 12; ++m, offset += 5)
	    {
		long idatum;
		if (!sscanf(line + offset, "%5ld", &idatum))
		{
		    printf("'%s' %d '%s'\n", line, offset, line + offset);
		    return NULL;
		}
		if (idatum == -9999)
		    idatum = IBAD;
		data[index][m] = idatum;
	    }
	}
	for (m = 0; m < 12; ++m)
	{
	    PyObject *pymonthly = PyList_New(years);
	    for (n = 0; n < years; ++n)
		PyList_SetItem(pymonthly, n,
			       PyFloat_FromDouble((double)(data[n][m]) * 0.1));
	    PyList_SetItem(pydata, m, pymonthly);
	}

	/* cleanup */
	for (n = 0; n < years; ++n)
	    PyMem_Free((void *)data[n]);
	PyMem_Free((void *)data);
    }
    retval = Py_BuildValue("Ol", pydata, begin);
    Py_DECREF(pydata);
    return retval;
}

static PyMethodDef stationstring_methods[] = {
	{"new",                 stationstring_new,		1},
	{"serialize",		stationstring_serialize,		1},
	{"from_lines",          stationstring_from_lines,       1},
	{NULL,		NULL}		/* sentinel */
};


/* Initialization function for the module (*must* be called initxx) */

void
initstationstring()
{
    PyObject *m, *d;
    /* Create the module and add the functions */
    m = Py_InitModule("stationstring", stationstring_methods);

    /* Add some symbolic constants to the module */
    d = PyModule_GetDict(m);
    StationStringError = PyErr_NewException("stationstring.error", NULL, NULL);
    if (StationStringError != NULL)
	PyDict_SetItemString(d, "error", StationStringError);
}


=========================================================

The analysis of this program will have to wait a fair while as I’m still finishing another step. It looks like it’s a well done library of utilities so I’ll likely leave it to the end.

Advertisements

About E.M.Smith

A technical managerial sort interested in things from Stonehenge to computer science. My present "hot buttons' are the mythology of Climate Change and ancient metrology; but things change...
This entry was posted in GISStemp Technical and Source Code and tagged , , , , , . Bookmark the permalink.