/*
Copyright (C) 2003 by Sean David Fleming

sean@ivec.org

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
of the License, 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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

The GNU GPL can also be found at http://www.gnu.org
*/

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "gdis.h"
#include "coords.h"
#include "error.h"
#include "file.h"
#include "parse.h"
#include "matrix.h"
#include "zmatrix.h"
#include "zmatrix_pak.h"
#include "model.h"
#include "interface.h"

gint set_true_false();
gint set_energy_units();
gint set_temperature_units();
gint set_length_units();
gint set_force_units();
gint set_pressure_units();
gint set_time_units();
gint set_mominert_units();

gint print_file_energy_units();
gint print_file_mass_units();
gint print_file_length_units();
gint print_file_time_units();
gint print_file_temperature_units();
gint print_file_pressure_units();
gint print_file_force_units();
gint print_file_mominert_units();

enum {SIESTA_DEFAULT, SIESTA_SPECIES, SIESTA_LATTICE_PARAMETERS, SIESTA_LATTICE_VECTORS,
      SIESTA_COORDS, SIESTA_ZMATRIX};

/* main structures */
extern struct sysenv_pak sysenv;
extern struct elem_pak elements[];


/**********************************/
/* obtain a core's species number */
/**********************************/
gint fdf_species_index(gchar *atom_label, GSList *species_list)
{
gint i;
struct species_pak *species;
GSList *list;

/* find corresponding number of this element in the species block */
i=1;
for (list=species_list ; list ; list=g_slist_next(list))
  {
  species = list->data;

  if (g_ascii_strcasecmp(atom_label, species->label) == 0)
    return(i);

  i++;
  }
return(0);
}

/*****************************************************/
/* generate the list of unique species, SIESTA style */
/*****************************************************/
GSList *fdf_species_build(struct model_pak *model)
{
gint code;
struct species_pak *species_data;
GSList *list, *normal_list, *ghost_list, *species_list;

/* init the normal, ghost, and overall species lists */
normal_list = find_unique(LABEL_NORMAL, model);
ghost_list = find_unique(LABEL_GHOST, model);
species_list = NULL;

for (list=normal_list ; list ; list=g_slist_next(list))
  {
  code = elem_symbol_test(list->data);

/* update the species identifier list */
  species_data = g_malloc(sizeof(struct species_pak));
  species_data->label = list->data;
  species_data->number = code;
  species_list = g_slist_prepend(species_list, species_data);
  }

for (list=ghost_list ; list ; list=g_slist_next(list))
  {
  code = elem_symbol_test(list->data);

/* update the species identifier list */
  species_data = g_malloc(sizeof(struct species_pak));
  species_data->label = list->data;
  species_data->number = -code;
  species_list = g_slist_prepend(species_list, species_data);
  }
species_list = g_slist_reverse(species_list);

/* parse the zmatrix entries and make the type match the (possible new) species order */
if (model->zmatrix)
  {
  struct zmat_pak *zmatrix = model->zmatrix;

/* scan zval entries - make sure type value reflects element order */
  for (list=zmatrix->zlines ; list ; list=g_slist_next(list))
    {
    struct zval_pak *zval = list->data;
    zval->type = fdf_species_index(zval->elem, species_list);
    }
  }

return(species_list);
}

/****************/
/* file writing */
/****************/
gint write_fdf(gchar *filename, struct model_pak *model)
{
gint i;
gdouble x[3], depth;
GSList *list, *clist, *species_list;
struct core_pak *core;
struct species_pak *species_data;
FILE *fp;

/* checks */
g_return_val_if_fail(model != NULL, 1);
g_return_val_if_fail(filename != NULL, 2);

/* open the file */
fp = fopen(filename,"wt");
if (!fp)
  return(3);

/* print header */
fprintf(fp, "# ");
gdis_blurb(fp);
fprintf(fp, "#\n\n");
fprintf(fp, "SystemLabel      %s\n\n", model->basename);
fprintf(fp, "NumberOfAtoms    %d\n\n", g_slist_length(model->cores));

/* init the normal, ghost, and overall species lists */
species_list = fdf_species_build(model);

fprintf(fp, "NumberOfSpecies  %d\n", g_slist_length(species_list));

/* write the species (unique atom types) block */
fprintf(fp, "%%block ChemicalSpeciesLabel\n");
i=1;
for (list=species_list ; list ; list=g_slist_next(list))
  {
  species_data = list->data;

  fprintf(fp, "  %3d  %3d  %s\n", i, species_data->number, species_data->label);
  i++;
  }
fprintf(fp, "%%endblock ChemicalSpeciesLabel\n\n");


/* write the lattice data */
if (model->periodic)
  {
  fprintf(fp, "LatticeConstant 1.0 Ang\n");
  if (model->construct_pbc)
    {
/* NB: siesta matrices are transposed wrt gdis */
    fprintf(fp, "%%block LatticeVectors\n");
    fprintf(fp,"%15.10f %15.10f %15.10f\n",
                model->latmat[0], model->latmat[3], model->latmat[6]);
    fprintf(fp,"%15.10f %15.10f %15.10f\n",
                model->latmat[1], model->latmat[4], model->latmat[7]);
    fprintf(fp,"%15.10f %15.10f %15.10f\n",
                model->latmat[2], model->latmat[5], model->latmat[8]);
    fprintf(fp, "%%endblock LatticeVectors\n\n");
    }
  else
    {
    fprintf(fp, "%%block LatticeParameters\n");
/* if it's a surface - print Dhkl (mult by region sizes) */
    if (model->periodic == 2)
      {
/* get depth info */
      depth = (model->surface.region[0]+model->surface.region[1])
                                          *model->surface.depth;
/* no depth info - make it large enough to fit everything */
      if (depth < POSITION_TOLERANCE)
        depth = 2.0*model->rmax;

      fprintf(fp, "  %f  %f  %f", model->pbc[0], model->pbc[1], depth);
      }
    else
      fprintf(fp, "  %f  %f  %f", model->pbc[0], model->pbc[1], model->pbc[2]);

    fprintf(fp, "  %f  %f  %f\n", R2D*model->pbc[3], R2D*model->pbc[4], R2D*model->pbc[5]);
    fprintf(fp, "%%endblock LatticeParameters\n\n");
    }
  }


/* write the zmatrix data */
/*
if (model->zmatrix)
*/
if (zmat_entries_get(model->zmatrix))
  {
  fprintf(fp, "ZM.UnitsLength %s\n", zmat_distance_units_get(model->zmatrix));
  fprintf(fp, "ZM.UnitsAngle %s\n\n", zmat_angle_units_get(model->zmatrix));

  fprintf(fp, "%%block Zmatrix\n");
 
  zmat_coord_print(fp, species_list, model);
 
  zmat_mol_print(fp, model->zmatrix);
 
  zmat_var_print(fp, model->zmatrix);

  zmat_const_print(fp, model->zmatrix);
 
  fprintf(fp, "%%endblock Zmatrix\n");
  }
else
  {

/* write the atoms - for surfaces (and isolated molecules), coords must be cartesian */
if (model->fractional)
  fprintf(fp, "AtomicCoordinatesFormat Fractional\n");
else
  fprintf(fp, "AtomicCoordinatesFormat NotScaledCartesianAng\n");

fprintf(fp, "%%block AtomicCoordinatesAndAtomicSpecies\n");

for (clist=model->cores ; clist ; clist=g_slist_next(clist))
  {
  core = clist->data;
  if (core->status & DELETED)
    continue;

  ARR3SET(x, core->x);

/* NB: want fractional if 3D periodic, otherwise cartesian */
  if (!model->fractional)
    vecmat(model->latmat, x);

/* find corresponding number of this element in the species block */
  i=1;
  for (list=species_list ; list ; list=g_slist_next(list))
    {
    species_data = list->data;

    if (g_ascii_strcasecmp(core->atom_label, species_data->label) == 0)
      {
      if (core->ghost)
        {
        if (species_data->number == -core->atom_code)
          break;
        }
      else
        {
        if (species_data->number == core->atom_code)
          break;
        }
      }
    i++;
    }

/* found? */
  if (list)
    fprintf(fp,"  %14.9f  %14.9f  %14.9f    %d\n", x[0], x[1], x[2], i);
  else
    printf("write_fdf() error: bad species type.\n");
  }
fprintf(fp, "%%endblock AtomicCoordinatesAndAtomicSpecies\n\n");

  }

free_slist(species_list);

/*
 * Siesta option writer
 */

//  PAO.BasisType
switch (model->siesta.basis_type)
{
    case SPLIT_PAOBT:
    {
        fprintf(fp, "PAO.BasisType    split\n");
        break;
    }
    case SPLITGAUSS_PAOBT:
    {
        fprintf(fp, "PAO.BasisType    splitgauss\n");
        break;
    }
    case NODES_PAOBT:
    {
        fprintf(fp, "PAO.BasisType    nodes\n");
        break;
    }
    case NONODES_PAOBT:
    {
        fprintf(fp, "PAO.BasisType    nonodes\n");
        break;
    }
    default:
    {
            //split!
        fprintf(fp, "PAO.BasisType    split\n");
        break;
    }
}



switch (model->siesta.basis_set)
{
    case SZ_ZETA :
    {
        fprintf(fp,"PAO.BasisSize    SZ\n");
        break;
    }
    case SZP_ZETA :
    {
        fprintf(fp,"PAO.BasisSize    SZP\n");
        break;
    }
    case DZ_ZETA :
    {
        fprintf(fp,"PAO.BasisSize    DZ\n");
        break;
    }
    case DZP_ZETA :
    {
        fprintf(fp,"PAO.BasisSize    DZP\n");
        break;
    }
    case CUSTOM_ZETA :
    {
        //TODO - CUSTOM Zeta Levels? - should set a tag somewhere
        fprintf(fp,"PAO.BasisSize    DZP\n");
        break;
    }
}


//siesta_solution_method_type
switch (model->siesta.solution_method_type)
{
    case DIAGON_SOLMETH: fprintf(fp, "SolutionMethod     diagon\n"); break;
    case ORDERN_SOLMETH: fprintf(fp, "SolutionMethod     OrderN\n"); break;
    default:
    //should never get here....
        error_table_entry("Inconsistancy model->siesta.solution_method_type.\n");
        fprintf(fp, "SolutionMethod     diagon\n");
        break;
}


//     if (model->siesta.split_zeta_norm != 0.15)
fprintf(fp, "PAO.SplitNorm    %f\n", model->siesta.split_zeta_norm);


fprintf(fp, "PAO.EnergyShift    %f ", model->siesta.energy_shift);
if (model->siesta.energy_shift_units)
  print_file_energy_units(fp, model->siesta.energy_shift_units);
else
  fprintf(fp, "\n");


//Default is FALSE.
if (model->siesta.harris_functional == TRUE)
    fprintf(fp, "Harris_functional    true\n");
else
    fprintf(fp, "Harris_functional    false\n");

//Default == LDA
switch (model->siesta.xc_functional_type)
{
    case GGA_XCFUNC: fprintf(fp, "XC.functional    GGA\n"); break;
    case LDA_XCFUNC: fprintf(fp, "XC.functional    LDA\n"); break;
    case LSD_XCFUNC: fprintf(fp, "XC.functional    LSD\n"); break;
    default:
        //should never get here
        error_table_entry("Inconsistancy model->siesta.xc_functional.\n");
        break;
}

switch (model->siesta.xc_author_type)
{
    case CA_XCAUTH:    fprintf(fp, "XC.Authors    CA\n"); break;
    case PZ_XCAUTH:    fprintf(fp, "XC.Authors    PZ\n"); break;
    case PW_XCAUTH:    fprintf(fp, "XC.Authors    PW92\n"); break;
    case PBE_XCAUTH:    fprintf(fp, "XC.Authors    PBE\n"); break;
    case RPBE_XCAUTH:    fprintf(fp, "XC.Authors    RPBE\n"); break;
    case LYP_XCAUTH:    fprintf(fp, "XC.Authors    LYP\n"); break;
    default:
        //should never get here
        error_table_entry("Inconsistancy model->siesta.xc_authors.\n");
        break;
}

if (model->siesta.spin_polarised == TRUE)
    fprintf (fp, "SpinPolarized    true\n");
else
    fprintf (fp, "SpinPolarized    false\n");



//MeshCutoff
fprintf (fp, "MeshCutoff    %f", model->siesta.mesh_cutoff);
if (print_file_energy_units(fp, model->siesta.mesh_cutoff_units) != 0)
{
    error_table_entry("Warning - Units missing MeshCutoff\n");
}

//kgrid_cutoff

fprintf(fp, "kgrid_cutoff    %f", model->siesta.kgrid_cutoff);
if (print_file_length_units(fp, model->siesta.kgrid_cutoff_units) !=0)
{
    error_table_entry("Warning - Units missing kgrid_cutoff\n");
}

fprintf(fp, "ElectronicTemperature    %f", model->siesta.electronic_temperature);
if (model->siesta.electronic_temperature_units != -1)
{
    if (print_file_temperature_units(fp, model->siesta.electronic_temperature_units) !=0)
    {
        error_table_entry("Warning - Units missing electronic_temperature_units\n");
    }
}


fprintf(fp, "MaxSCFIterations    %d\n", (int) model->siesta.no_of_cycles);
fprintf(fp, "DM.NumberPulay    %d\n", (int) model->siesta.no_of_pulay_matrices);
fprintf(fp, "DM.MixingWeight    %f\n", model->siesta.mixing_weight);

if (model->siesta.pulay_mixing)
{
    //what does this map to?
}


switch (model->siesta.run_type)
{
    case SINGLE_POINT:
        fprintf(fp, "MD.TypeOfRun    CG\n");
        break;
    case OPTIMISATION: fprintf(fp, "MD.TypeOfRun    Verlet\n"); break;
    case MOLECULAR_DYNAMICS: fprintf(fp, "MD.TypeOfRun    Verlet\n"); break;
    case PHONON_CALCULATION: 
        switch (model->siesta.md_type_of_run)
        {
            case FC_MDRUN:      fprintf(fp, "MD.TypeOfRun    FC\n"); break;
            case PHONON_MDRUN:  fprintf(fp, "MD.TypeOfRun    Phonon\n"); break;
            default:
            break;
        }
        break;        
                

    //MORE OPTIONS HERE > see manual
    default :
        fprintf(fp," \n");
        error_table_entry("Warning - MD.TypeOfRun *NOT* written out\n");
        break;
}


if (model->siesta.md_variable_cell)
    fprintf(fp, "MD.VariableCell    true\n");
else
    fprintf(fp, "MD.VariableCell    false\n");

if (model->siesta.md_num_cg_steps != -1.0)
{
    fprintf(fp, "MD.NumCGsteps    %d\n", (gint) model->siesta.md_num_cg_steps);
}

fprintf(fp, "MD.MaxCGDispl    %f", model->siesta.md_max_cg_displacement);
if (print_file_length_units(fp, model->siesta.md_max_cg_displacement_units) != 0)
{
    error_table_entry("Warning - Units missing MD.MaxCGDispl\n");
}


fprintf(fp, "MD.PreconditionVariableCell    %f", model->siesta.md_precondition_variable_cell);
if (print_file_length_units(fp, model->siesta.md_precondition_variable_cell_units ) != 0)
{
    error_table_entry("Warning - Units missing MD.PreconditionVariableCell\n");
}


fprintf(fp, "MD.MaxStressTol    %f", model->siesta.md_max_stress_tol);
if (print_file_pressure_units(fp, model->siesta.md_max_stress_tol_units) != 0)
{
    error_table_entry("Warning - Units missing MD.MaxStressTol\n");
}

fprintf(fp, "\n%%block MD.TargetStress\n");
fprintf(fp, "%f %f %f %f %f %f\n", model->siesta.md_target_stress_xx,
                                   model->siesta.md_target_stress_yy,
                                   model->siesta.md_target_stress_zz,
                                   model->siesta.md_target_stress_xy,
                                   model->siesta.md_target_stress_xz,
                                   model->siesta.md_target_stress_yz);
fprintf(fp, "%%endblock MD.TargetStress\n\n");



fprintf(fp, "MD.MaxForceTol    %f", model->siesta.md_max_force_tol);
if (print_file_force_units(fp, model->siesta.md_max_force_tol_units) != 0)
{
    error_table_entry("Warning - Units missing MD.MaxForceTol\n");
}

//run_type is not CG_MDRUN
fprintf(fp, "MD.InitialTimeStep    %d\n", (gint) model->siesta.md_inital_time_step);
fprintf(fp, "MD.FinalTimeStep    %d\n", (gint) model->siesta.md_final_time_step);
fprintf(fp, "MD.LengthTimeStep    %d", (gint) model->siesta.timestep);
if (print_file_time_units(fp, model->siesta.timestep_units) != 0)
{
    error_table_entry("Warning - Units missing MD.LengthTimeStep\n");
}

fprintf(fp, "MD.InitialTemperature    %f", model->siesta.md_inital_temperature);
if (print_file_temperature_units(fp, model->siesta.md_inital_temperature_units) != 0)
{
    error_table_entry("Warning - Units missing MD.InitialTemperature\n");
}

if (model->siesta.md_quench)
    fprintf(fp, "MD.Quench    true\n");
else
    fprintf(fp, "MD.Quench    false\n");

fprintf(fp, "MD.TargetTemperature    %f", model->siesta.md_inital_temperature);
if (print_file_temperature_units(fp, model->siesta.md_inital_temperature_units) !=0)
{
    error_table_entry("Warning - Units missing MD.TargetTemperature\n");
}


fprintf(fp, "MD.NoseMass    %f", model->siesta.md_nose_mass);
if (print_file_mominert_units(fp, model->siesta.md_nose_mass_units) != 0)
{
    error_table_entry("Warning - Units missing MD.NoseMass\n");
}

fprintf(fp, "MD.ParrinelloRahmanMass    %f", model->siesta.md_parrinello_rahman_mass);
if (print_file_mominert_units(fp, model->siesta.md_parrinello_rahman_mass_units) != 0)
{
    error_table_entry("Warning - Units missing MD.ParrinelloRahmanMass\n");
}

switch (model->siesta.md_anneal_option)
{
    case TEMPERATURE_ANNEAL:
        fprintf(fp, "MD.AnnealOption    Temperature\n");
        break;
    case PRESSURE_ANNEAL:
        fprintf(fp, "MD.AnnealOption    Pressure\n");
        break;
    case TEMP_AND_PRESS_ANNEAL:
        fprintf(fp, "MD.AnnealOption    TemperatureandPressure\n");
        break;
    default:
        fprintf(fp, "MD.AnnealOption    TemperatureandPressure\n");
        break;
}

fprintf(fp, "MD.TauRelax    %f", model->siesta.md_tau_relax);
if (print_file_time_units(fp, model->siesta.md_tau_relax_units) !=0)
{
    error_table_entry("Warning - Units missing MD.TauRelax\n");
}

fprintf(fp, "MD.BulkModulus    %f", model->siesta.md_bulk_modulus);
if (print_file_pressure_units(fp, model->siesta.md_bulk_modulus_units) !=0)
{
    error_table_entry("Warning - Units missing MD.BulkModulus\n");
}


fprintf(fp, "MD.TargetPressure    %f", model->siesta.md_target_pressure);
if (print_file_pressure_units(fp, model->siesta.md_target_pressure_units) !=0)
{
    error_table_entry("Warning - Units missing MD.BulkModulus\n");
}


fprintf(fp, "MD.FCDispl    %f", model->siesta.md_fc_displ);
if (print_file_length_units(fp, model->siesta.md_fc_displ_units) !=0)
{
    error_table_entry("Warning - Units missing MD.FCDispl\n");
}
fprintf(fp, "MD.FCfirst    %d\n", model->siesta.md_fc_first );
fprintf(fp, "MD.FClast    %d\n\n", model->siesta.md_fc_last);


if (model->siesta.use_saved_data)
    fprintf(fp, "UseSaveData\t\ttrue\n");
else
    fprintf(fp, "UseSaveData\t\tfalse\n");


if (model->siesta.file_output_write_coor_init)          //WriteCoorInital
    fprintf(fp, "WriteCoorInital    true\n");
else
    fprintf(fp, "WriteCoorInital    false\n");

if (model->siesta.file_output_write_coor_step)          //WriteCoorStep
    fprintf(fp, "WriteCoorStep    true\n");
else
    fprintf(fp, "WriteCoorStep    false\n");

if (model->siesta.file_output_write_forces)             //WriteForces
    fprintf(fp, "WriteForces\t\ttrue\n");
else
    fprintf(fp, "WriteForces\t\tfalse\n");

if (model->siesta.file_output_write_kpoints)            //WriteKpoints
    fprintf(fp, "WriteKpoints\t\ttrue\n");
else
    fprintf(fp, "WriteKpoints\t\tfalse\n");

if (model->siesta.file_output_write_eigenvalues)        //WriteEigenvalues
    fprintf(fp, "WriteEigenvalues\t\ttrue\n");
else
    fprintf(fp, "WriteEigenvalues\t\tfalse\n");

if (model->siesta.file_output_write_kbands)             //WriteKbands
    fprintf(fp, "WriteKbands\t\ttrue\n");
else
    fprintf(fp, "WriteKbands\t\tfalse\n");

if (model->siesta.file_output_write_bands)              //WriteBands
    fprintf(fp, "WriteBands\t\ttrue\n");
else
    fprintf(fp, "WriteBands\t\tfalse\n");

if (model->siesta.file_output_write_wavefunctions)      //WriteWaveFunctions
    fprintf(fp, "WriteWaveFunctions\t\ttrue\n");
else
    fprintf(fp, "WriteWaveFunctions\t\tfalse\n");

if (model->siesta.file_output_write_mullikenpop != -1)         //WriteMullikenPop
    fprintf(fp, "WriteMullikenPop\t\t%d\n", (int) model->siesta.file_output_write_mullikenpop);

if (model->siesta.file_output_write_dm)                 //WriteDM
    fprintf(fp, "WriteDM\t\ttrue\n");
else
    fprintf(fp, "WriteDM\t\tfalse\n");

if (model->siesta.file_output_write_coor_xmol)          //WriteCoorXmol
    fprintf(fp, "WriteCoorXmol\t\ttrue\n");
else
    fprintf(fp, "WriteCoorXmol\t\tfalse\n");

if (model->siesta.file_output_write_coor_cerius)        //WriteCoorCerius
    fprintf(fp, "WriteCoorCerius\t\ttrue\n");
else
    fprintf(fp, "WriteCoorCerius\t\tfalse\n");

if (model->siesta.file_output_write_md_xmol)            //WriteMDXmol
    fprintf(fp, "WriteMDXmol\t\ttrue\n");
else
    fprintf(fp, "WriteMDXmol\t\tfalse\n");

if (model->siesta.file_output_write_md_history)         //WriteMDhistory
    fprintf(fp, "WriteMDhistory\t\ttrue\n");
else
    fprintf(fp, "WriteMDhistory\t\tfalse\n");

/*
g_slist_free(normal_list);
g_slist_free(ghost_list);
*/

fclose(fp);
return(0);
}

/****************************************************/
/* attempt to read header at a particular precision */
/****************************************************/
gint siesta_header_read(FILE *fp, gint precision, gdouble *cell, gint *grid)
{
gint i, swap=FALSE;
int grid_int[4];
/*long grid_long[4];*/
/*float cell_float[9];*/
double cell_double[9];

/* standard return values */
for (i=9 ; i-- ; )
  cell[i] = 0.0;
for (i=4 ; i-- ; )
  grid[i] = 0;

/* precision dependent read */
switch (precision)
  {
  case 1:
    READ_RECORD;
    fread(cell_double, sizeof(double), 9, fp);
    READ_RECORD;

    READ_RECORD;
    fread(&grid_int, sizeof(int), 4, fp);
    READ_RECORD;

/* byte ordering check */
    for (i=9 ; i-- ; )
      {
      if (fabs(cell_double[i]) > 1000000.0)
        {
        swap = TRUE;
        break;
        }
      }
    if (swap)
      {
      for (i=9 ; i-- ; )
        swap_bytes(&cell_double[i], sizeof(double));
      for (i=4 ; i-- ; )
        swap_bytes(&grid_int[i], sizeof(int));
      }

/* pass back */
    for (i=9 ; i-- ; )
      cell[i] = cell_double[i];
    for (i=4 ; i-- ; )
      grid[i] = grid_int[i];
    break;

  default:
    printf("FIXME - unsuported precision.\n");
  }

return(swap);
}

/******************************************/
/* check for additional siesta data files */
/******************************************/
#define DEBUG_SIESTA_DENSITY 0
void seista_density_files(const gchar *source, struct model_pak *model)
{
gint grid[4];
gint i, j, k, n, size, file_size, type, swap;
gchar *text, *name;
float *f;
gdouble cell[9], icell[9], *ptr;
FILE *fp;

g_assert(source != NULL);
g_assert(model != NULL);

#if DEBUG_SIESTA_DENSITY
printf("source: %s\n", source);
#endif

/* search for siesta output types */
/* in same directory & with same basename - different extension */
for (type=0 ; type<2 ; type++)
  {
/* remove extension */
  name = text = NULL;
  n = strlen(source);
  for (i=n ; i-- ; )
    {
    if (*(source+i) == '.')
      {
      text = g_strndup(source, i);
      break;
      }
    }
/* should never get a name with no extension */
  if (!text)
    return;

/* build current type name */
  switch (type)
    {
    case 0:
      name = g_strdup_printf("%s.RHO", text);
      break;

    case 1:
      name = g_strdup_printf("%s.VH", text);
      break;
    }
  g_free(text);
  if (!name)
    continue;

  fp = fopen(name, "rt");
  if (!fp)
    {
#if DEBUG_SIESTA_DENSITY
printf("Could not find: %s\n", name);
#endif
    continue;
    }

  file_size = file_byte_size(name);
  g_free(name);

  swap = siesta_header_read(fp, 1, cell, grid);

  memcpy(icell, cell, 9*sizeof(gdouble));
  matrix_invert(icell);

#if DEBUG_SIESTA_DENSITY
printf("swap bytes: %d\n", swap);
P3MAT(" cell: ", cell);
P3MAT("icell: ", icell);
printf("grid: %d x %d x %d : %d\n", grid[0], grid[1], grid[2], grid[3]);
#endif

/* NB: FORTRAN has a 4 byte separator either side of each record (single write() call) */
/*
real*8 cell(3,3)
real*4 rho(nmesh(1)*nmesh(2)*nmesh(3),nspin)
integer mesh(3)
integer nspin

    write(iu) cell
    write(iu) mesh, nspin
    ind = 0
    do is  = 1,nspin
       do iz = 1,mesh(3)
         do iy = 1,mesh(2)
            write(iu) rho(ind+ix,is),ix = 1,mesh(1)
            ind = ind + mesh(1)
         enddo
       enddo
    enddo
*/

/* data array size */
  n = grid[1] * grid[2] * grid[3];
  size = 8 + 4*grid[0];
  size *= n;
/* cell + mesh size */
  size += 104;

#if DEBUG_SIESTA_DENSITY
printf("Data size: %d bytes\n", size);
printf("File size: %d bytes\n", file_size);
#endif

  if (size != file_size)
    {
/* TODO - retry at different precision */
    gui_text_show(ERROR, "Failed to parse binary file.\n");
    continue;
    }

  memcpy(model->siesta.cell, cell, 9*sizeof(gdouble));
  memcpy(model->siesta.icell, icell, 9*sizeof(gdouble));
  memcpy(model->siesta.grid, grid, 4*sizeof(gint));

/* CURRENT - test for a scaling factor (ie au output instead of angstrom) */
matmat(model->latmat, icell);

if (icell[0] > 0.2)
  {
#if DEBUG_SIESTA_DENSITY
printf("density grid in au, scale = %f\n", icell[0]);
#endif
  model->siesta.eden_scale = icell[0];
  }

/* CURRENT - read function data */
  switch (type)
    {
    case 0:
      model->siesta.eden = g_malloc(n*grid[0]*sizeof(gdouble));
      ptr = model->siesta.eden;
      break;

    case 1:
      model->siesta.epot = g_malloc(n*grid[0]*sizeof(gdouble));
      ptr = model->siesta.epot;
      break;

    default:
      ptr = NULL;
    }

  f = g_malloc(grid[0]*sizeof(float));
  k=0;
  for (i=0 ; i<n ; i++)
    {
    READ_RECORD;
    fread(f, sizeof(float), grid[0], fp);
    READ_RECORD;

    if (ptr)
      {
      if (swap)
        {
        for (j=0 ; j<grid[0] ; j++)
          {
          swap_bytes(f+j, sizeof(float));
          *(ptr+k++) = *(f+j);
          }
        }
      else
        {
        for (j=0 ; j<grid[0] ; j++)
          *(ptr+k++) = *(f+j);
        }
      }

/*
        *(model->siesta.eden+k++) = *(f+j);
*/

/* DEBUG */
/*
if (i == n-1)
  {
  for (j=0 ; j<grid[0] ; j++)
    printf("%f\n", *(model->siesta.eden+j));
  }
*/
    }
  g_free(f);

/* this shouldn't happen (file moved while reading?) */
  if (k != n*grid[0])
    {
    gui_text_show(ERROR, "Fatal error reading binary file.\n");
/*
  g_free(model->siesta.eden);
  model->siesta.eden = NULL;
*/
    }
  }
}

/***********************************************************************/
/* process lattice constant line to produce a cartesian scaling factor */
/***********************************************************************/
#define DEBUG_SIESTA_SCALE 0
gdouble siesta_scale_parse(const gchar *text)
{
gint num_tokens;
gchar **buff;
gdouble scale = 1.0;

g_assert(text != NULL);

#if DEBUG_SIESTA_SCALE
printf("input line: %s", text);
#endif

/* process scale */
buff = tokenize(text, &num_tokens);
if (num_tokens > 1)
  scale *= str_to_float(*(buff+1));

/* process units */
if (num_tokens > 2)
  if ((g_ascii_strncasecmp(*(buff+2), "au", 2) == 0) ||
      (g_ascii_strncasecmp(*(buff+2), "Bohr", 4) == 0))
    scale *= AU2ANG;  

g_strfreev(buff);

#if DEBUG_SIESTA_SCALE
printf("output scale: %f\n", scale);
#endif

return(scale);
}

/********************************/
/* process a species block line */
/********************************/
void seista_parse_species(const gchar *line, GSList **list)
{
gint num_tokens;
gchar **buff;
struct species_pak *species;

g_assert(line != NULL);

buff = tokenize(line, &num_tokens);

if (num_tokens > 2)
  {
  species = g_malloc(sizeof(struct species_pak));

  species->number = str_to_float(*(buff+1));
  species->label = g_strdup(*(buff+2));
  *list = g_slist_append(*list, species);
  }

g_strfreev(buff);
}

/************************************/
/* process a lattice parameter line */
/************************************/
void seista_parse_cell(const gchar *line, gdouble scale, struct model_pak *model)
{
gint num_tokens;
gchar **buff;

g_assert(line != NULL);

buff = tokenize(line, &num_tokens);
if (num_tokens > 5)
  {
  model->pbc[0] = scale * str_to_float(*(buff+0));
  model->pbc[1] = scale * str_to_float(*(buff+1));
  model->pbc[2] = scale * str_to_float(*(buff+2));
  model->pbc[3] = D2R*str_to_float(*(buff+3));
  model->pbc[4] = D2R*str_to_float(*(buff+4));
  model->pbc[5] = D2R*str_to_float(*(buff+5));
  }

model->construct_pbc = FALSE;

/* hack for determining if it's a (GDIS created) surface */
if (fabs(model->pbc[2]) < FRACTION_TOLERANCE)
  model->periodic = 2;
else
  model->periodic = 3;

g_strfreev(buff);
}

/*********************************/
/* process a lattice vector line */
/*********************************/
void seista_parse_lattice(const gchar *line, gint n, gdouble scale, struct model_pak *model)
{
gint num_tokens;
gchar **buff;

g_assert(line != NULL);

buff = tokenize(line, &num_tokens);
if (num_tokens > 2)
  {
  model->latmat[0+n] = scale * str_to_float(*(buff+0));
  model->latmat[3+n] = scale * str_to_float(*(buff+1));
  model->latmat[6+n] = scale * str_to_float(*(buff+2));
  }

model->construct_pbc = TRUE;
model->periodic = 3;

g_strfreev(buff);
}

/***********************************/
/* process a coordinate block line */
/***********************************/
void seista_parse_coords(const gchar *line, GSList *species_list, gdouble scale, struct model_pak *model)
{
gint i, num_tokens;
gchar **buff;
struct species_pak *species;
struct core_pak *core;

g_assert(line != NULL);

buff = tokenize(line, &num_tokens);

if (num_tokens > 3)
  {
/* find corresponding number of this element in the species block */
/* NB: list counts from 0, fdf counts from 1 (hence the minus 1) */
  if (species_list)
    {
    i = abs((gint) str_to_float(*(buff+3)) - 1);
    species = g_slist_nth_data(species_list, i);

    core = new_core(species->label, model);
    
/* translucent ghost atom */
    if (species->number < 0)
      {
      core->ghost = TRUE;
      core->colour[3] = 0.5;
      }
    }
  else
    core = new_core("X", model);
 
  model->cores = g_slist_prepend(model->cores, core);
    
  core->x[0] = str_to_float(*(buff+0));
  core->x[1] = str_to_float(*(buff+1));
  core->x[2] = str_to_float(*(buff+2));

  VEC3MUL(core->x, scale);
  }

g_strfreev(buff);
}

/***************************/
/* zmatrix line processing */
/***************************/
void seista_parse_zmatrix(const gchar *text,
                          GSList *species_list,
                          gdouble scale,
                          struct model_pak *model)
{
gint i, num_tokens;
gchar **buff;
struct core_pak *core;
struct species_pak *species_data;
static gint state=0;

/* keyword processing within zmatrix block */
if (g_ascii_strncasecmp("molecule", text, 8) == 0)
  {
  state = 1;
  if (strstr(text, "frac"))
    zmat_fractional_set(model->zmatrix);
  else
    zmat_cartesian_set(model->zmatrix);
  return;
  }

if (g_ascii_strncasecmp("variable", text, 8) == 0)
  {
  state = 2;
  return;
  }

if (g_ascii_strncasecmp("constant", text, 8) == 0)
  {
  state = 3;
  return;
  }

if (g_ascii_strncasecmp("fractional", text, 10) == 0)
  {
  state = 4;
  model->fractional = TRUE;
  return;
  }

if (g_ascii_strncasecmp("cartesian", text, 9) == 0)
  {
  state = 4;
  model->fractional = FALSE;
  return;
  }

/* data processing within zmatrix block */
switch (state)
  {
  case 1:
/* process zmatrix coords */
    zmat_core_add(text, model->zmatrix);
    break;

  case 2:
/* process zmatrix variables */
    zmat_var_add(text, model->zmatrix);
    break;

  case 3:
/* process zmatrix constants */
    zmat_const_add(text, model->zmatrix);
    break;

  case 4:
/* fractional input coords */
    buff = tokenize(text, &num_tokens);
    if (num_tokens > 3)
      {
/* find corresponding number of this element in the species block */
/* NB: list counts from 0, fdf counts from 1 (hence the minus 1) */
      if (species_list)
        {
        i = abs((gint) str_to_float(*(buff)) - 1);
        species_data = g_slist_nth_data(species_list, i);

        core = new_core(species_data->label, model);

/* translucent ghost atom */
        if (species_data->number < 0)
          {
          core->ghost = TRUE;
          core->colour[3] = 0.5;
          }
        }
      else
        core = new_core("X", model);

      model->cores = g_slist_prepend(model->cores, core);

      core->x[0] = scale * str_to_float(*(buff+1));
      core->x[1] = scale * str_to_float(*(buff+2));
      core->x[2] = scale * str_to_float(*(buff+3));
      }
    g_strfreev(buff);
    break;
  }
}

/******************************/
/* main fdf data block reader */
/******************************/
#define DEBUG_READ_FDF_BLOCK 0
gint read_fdf_block(FILE *fp, struct model_pak *model, gboolean read_inital_cooords)
{
gint count, num_tokens, block_type;
gchar **buff, *line;
gdouble scale_lattice = 1.0, scale_coords = 1.0;
GSList *species_list=NULL;

g_assert(fp != NULL);
g_assert(model != NULL);

for (;;)
  {
  line = file_read_line(fp);

/* terminate if NULL returned */
  if (!line)
    break;

  buff = tokenize(line, &num_tokens);

/* get next line if blank */
  if (!buff)
    continue;

/* NEW - block/enblock processing */
  if (g_ascii_strncasecmp("\%block", line, 6) == 0)
    {
    block_type = SIESTA_DEFAULT;

    if (num_tokens > 1)
      {
      if (g_ascii_strncasecmp("ChemicalSpecies", *(buff+1), 15) == 0)
        block_type = SIESTA_SPECIES;
      if (g_ascii_strncasecmp("LatticeParameters", *(buff+1), 17) == 0)
        block_type = SIESTA_LATTICE_PARAMETERS;
      if (g_ascii_strncasecmp("LatticeVectors", *(buff+1), 14) == 0)
        block_type = SIESTA_LATTICE_VECTORS;
      if (g_ascii_strncasecmp("AtomicCoordinates", *(buff+1), 17) == 0)
        block_type = SIESTA_COORDS;
      if (g_ascii_strncasecmp("zmatrix", *(buff+1), 7) == 0)
        {
        block_type = SIESTA_ZMATRIX;
        }
      }

#if DEBUG_READ_FDF_BLOCK
printf("processing block [type %d]\n", block_type);
#endif

    count = 0;
    for (;;)
      {
      line = file_read_line(fp);
      if (!line)
        goto siesta_done_file;
      if (g_ascii_strncasecmp("\%endblock", line, 9) == 0)
        {
#if DEBUG_READ_FDF_BLOCK
printf("end of block.\n");
#endif
        goto siesta_done_line;
        }

      switch (block_type)
        {
        case SIESTA_SPECIES:
          seista_parse_species(line, &species_list);
          break;

        case SIESTA_LATTICE_PARAMETERS:
          seista_parse_cell(line, scale_lattice, model);
          break;

        case SIESTA_LATTICE_VECTORS:
          seista_parse_lattice(line, count, scale_lattice, model);
          break;

        case SIESTA_COORDS:
          seista_parse_coords(line, species_list, scale_coords, model);
          break;

        case SIESTA_ZMATRIX:
          seista_parse_zmatrix(line, species_list, scale_coords, model);
          break;

        default:
          error_table_entry("Unrecognized block encountered.\n");
          break;
        }
      count++;
      }
/* done block processing - get next line */
    continue;
    }

/* cartesian/fractional */
  if (read_inital_cooords)
    {
    if (g_ascii_strncasecmp("AtomicCoordinatesFormat", line, 23) == 0)
      {
      if (g_strrstr(line, "ractional"))
        model->fractional = TRUE;
      else
        {
        model->fractional = FALSE;
/* NEW */
        if (g_strrstr(line, "Bohr"))
          scale_coords = AU2ANG;
        }
      }
    }

/* scaling */
  if (g_ascii_strncasecmp("LatticeConstant", *buff, 15) == 0)
    scale_lattice = siesta_scale_parse(line);

  if (g_ascii_strncasecmp("NumberOfAtoms", *buff, 13) == 0)
    {
    if (num_tokens > 1)
      model->siesta.num_atoms = (gint) str_to_float(*(buff+1));
    }

  if (g_ascii_strncasecmp("SystemLabel", *buff, 11) == 0)
    {
    if (num_tokens > 1)
      {
      g_free(model->basename);
      model->basename = g_strdup(*(buff+1));
      }
    }

//Terry Siesta options - arrrgh!

  if (g_ascii_strncasecmp("kgrid_cutoff", *buff, 12) == 0)
    {
    if (num_tokens >= 3)             //kgrid_cutoff real length_type #othercrap
      {
      model->siesta.kgrid_cutoff = str_to_float(*(buff+1));
      if (set_length_units(*(buff+2), &model->siesta.kgrid_cutoff_units) !=0)
        error_table_entry("Units missing kgrid_cutoff.\n");
      }
    else
      error_table_entry("kgrid_cutoff line corrupt.\n");
    }

  if (g_ascii_strncasecmp("WriteCoorInitial", *buff, 16) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
          model->siesta.file_output_write_coor_init = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
          model->siesta.file_output_write_coor_init = TRUE;
      else
          model->siesta.file_output_write_coor_init = FALSE;
      }
    else
      {
      error_table_entry("WriteCoorInitial line corrupt\n");
      }
    }

  if (g_ascii_strncasecmp("WriteCoorStep", *buff, 13) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_coor_step = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_coor_step = TRUE;
      else
        model->siesta.file_output_write_coor_step = FALSE;
      }
    else
      error_table_entry("WriteCoorStep line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteForces", *buff, 11) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_forces = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_forces = TRUE;
      else
        model->siesta.file_output_write_forces = FALSE;
      }
    else
      error_table_entry("WriteForcesStep line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteKpoints", *buff, 12) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_kpoints = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_kpoints = TRUE;
      else
        model->siesta.file_output_write_kpoints = FALSE;
      }
    else
      error_table_entry("WriteKpoints line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteEigenvalues", *buff, 16) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_eigenvalues = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_eigenvalues = TRUE;
      else
        model->siesta.file_output_write_eigenvalues = FALSE;
      }
    else
      error_table_entry("WriteEigenvalues line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteKbands", *buff, 11) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_kbands = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_kbands = TRUE;
      else
        model->siesta.file_output_write_kbands = FALSE;
      }
    else
      error_table_entry("WriteKbands line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteBands", *buff, 10) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_bands = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_bands = TRUE;
      else
        model->siesta.file_output_write_bands = FALSE;
      }
    else
      error_table_entry("WriteBands line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteWaveFunctions", *buff, 18) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_wavefunctions = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_wavefunctions = TRUE;
      else
        model->siesta.file_output_write_wavefunctions = FALSE;
      }
    else
      error_table_entry("WriteWaveFunctions line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteDM", *buff, 7) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_dm = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_dm = TRUE;
      else
        model->siesta.file_output_write_dm = FALSE;
      }
    else
      error_table_entry("WriteDM line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteCoorXmol", *buff, 13) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_coor_xmol = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_coor_xmol = TRUE;
      else
        model->siesta.file_output_write_coor_xmol = FALSE;
      }
    else
      error_table_entry("WriteCoorXmol line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteCoorCerius", *buff, 15) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_coor_cerius = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_coor_cerius = TRUE;
      else
        model->siesta.file_output_write_coor_cerius = FALSE;
      }
    else
      error_table_entry("WriteCoorCerius line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteMDXmol", *buff, 11) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_md_xmol = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_md_xmol = TRUE;
      else
        model->siesta.file_output_write_md_xmol = FALSE;
      }
    else
      error_table_entry("WriteMDXmol line corrupt\n");
    }

  if (g_ascii_strncasecmp("WriteMDhistory", *buff, 14) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.file_output_write_md_history = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.file_output_write_md_history = TRUE;
      else
        model->siesta.file_output_write_md_history = FALSE;
      }
    else
      error_table_entry("WriteMDhistory line corrupt\n");
    }
  
  if (g_ascii_strncasecmp("LongOutput", *buff, 10) == 0)
    {
    if (num_tokens >= 2)
      {
      if ((g_ascii_strncasecmp("true", *buff, 4) == 0) ||
         (g_ascii_strncasecmp(".true", *buff, 5) == 0) )
        {
        model->siesta.long_output = TRUE;
        model->siesta.file_output_write_coor_step = TRUE;
        model->siesta.file_output_write_forces = TRUE;
        model->siesta.file_output_write_kpoints = TRUE;
        model->siesta.file_output_write_eigenvalues = TRUE;
        model->siesta.file_output_write_bands = TRUE;
        model->siesta.file_output_write_kbands = TRUE;
        model->siesta.file_output_write_wavefunctions = TRUE;
        model->siesta.file_output_write_mullikenpop = 1.0;
        }
      else
        {
        model->siesta.long_output = FALSE;
        model->siesta.file_output_write_coor_step = FALSE;
        model->siesta.file_output_write_forces = FALSE;
        model->siesta.file_output_write_kpoints = FALSE;
        model->siesta.file_output_write_eigenvalues = FALSE;
        model->siesta.file_output_write_bands = FALSE;
        model->siesta.file_output_write_kbands = FALSE;
        model->siesta.file_output_write_wavefunctions = FALSE;
        model->siesta.file_output_write_mullikenpop = 0.0;
        }
      }
    else
      {
      model->siesta.long_output = FALSE;
      model->siesta.file_output_write_coor_step = FALSE;
      model->siesta.file_output_write_forces = FALSE;
      model->siesta.file_output_write_kpoints = FALSE;
      model->siesta.file_output_write_eigenvalues = FALSE;
      model->siesta.file_output_write_bands = FALSE;
      model->siesta.file_output_write_kbands = FALSE;
      model->siesta.file_output_write_wavefunctions = FALSE;
      model->siesta.file_output_write_mullikenpop = 0.0;

      error_table_entry("LongOutput line corrupt\n");
      }
    }

  if (g_ascii_strncasecmp("PAO.SplitNorm", *buff, 13) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.split_zeta_norm = str_to_float(*(buff+1));
    else
      error_table_entry("PAO.SplitNorm line corrupt : using defaults.\n");
    }

  if (g_ascii_strncasecmp("PAO.BasisSize", *buff, 13) == 0)
    {
    if (num_tokens == 2)
      {
      if (g_ascii_strncasecmp("SZ", *(buff+1), 2) == 0)
          model->siesta.basis_set = SZ_ZETA;

      if (g_ascii_strncasecmp("SZP", *(buff+1), 3) == 0)
        model->siesta.basis_set = SZP_ZETA;

      if (g_ascii_strncasecmp("DZ", *(buff+1), 2) == 0)
        model->siesta.basis_set = DZ_ZETA;

      if (g_ascii_strncasecmp("DZP", *(buff+1), 3) == 0)
        model->siesta.basis_set = DZP_ZETA;
      }
    else
      {
      model->siesta.basis_set = DZP_ZETA;          
      error_table_entry("PAO.BasisSize line corrupt : using defaults\n");
      }
    }
  
  if (g_ascii_strncasecmp("PAO.EnergyShift", *buff, 15) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.energy_shift = str_to_float(*(buff+1));
      if (set_energy_units(*(buff+2), &model->siesta.energy_shift_units) != 0)
        error_table_entry("PAO.EnergyShift line corrupt : using defaults\n");
      }
    else
      error_table_entry("PAO.EnergyShift line corrupt : using defaults\n");
    }
  
  if (g_ascii_strncasecmp("SolutionMethod", *buff, 14) == 0)
    {
    if (g_ascii_strncasecmp("OrderN", *(buff+1), 6) == 0)
      model->siesta.solution_method_type = ORDERN_SOLMETH;
    else if (g_ascii_strncasecmp("diagon", *(buff+1), 6) == 0)
      model->siesta.solution_method_type = DIAGON_SOLMETH;
    else
      error_table_entry("SolutionMethod line corrupt\n");
    }

  if (g_ascii_strncasecmp("XC.functional", *buff, 13) == 0)
    {
    if (g_ascii_strncasecmp("LDA", *(buff+1), 3) == 0)
        model->siesta.xc_functional_type = LDA_XCFUNC;
    else if (g_ascii_strncasecmp("LSD", *(buff+1), 3) == 0)
        model->siesta.xc_functional_type = LSD_XCFUNC;
    else if (g_ascii_strncasecmp("GGA", *(buff+1), 3) == 0)
        model->siesta.xc_functional_type = GGA_XCFUNC;
    else
      error_table_entry("XC.functional line corrupt\n");
    }

  if (g_ascii_strncasecmp("XC.authors", *buff, 10) == 0)
    {
    if (g_ascii_strncasecmp("PZ", *(buff+1), 2) == 0)
        model->siesta.xc_author_type = PZ_XCAUTH;
    else if (g_ascii_strncasecmp("CA", *(buff+1), 2) == 0)
        model->siesta.xc_author_type = CA_XCAUTH;
    else if (g_ascii_strncasecmp("PW92", *(buff+1), 4) == 0)
        model->siesta.xc_author_type = PW_XCAUTH;
    else if (g_ascii_strncasecmp("PBE", *(buff+1), 3) == 0)
        model->siesta.xc_author_type = PBE_XCAUTH;
    else if (g_ascii_strncasecmp("RPBE", *(buff+1), 4) == 0)
        model->siesta.xc_author_type = RPBE_XCAUTH;
    else if (g_ascii_strncasecmp("LYP", *(buff+1), 4) == 0)
        model->siesta.xc_author_type = LYP_XCAUTH;
    else
      error_table_entry("XC.authors line corrupt\n");
    }

//mesh_cutoff -> MeshCutoff real siesta_unit_type
  if (g_ascii_strncasecmp("MeshCutoff", *buff, 10) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.mesh_cutoff = str_to_float(*(buff+1));
      if (set_energy_units(*(buff+2), &model->siesta.mesh_cutoff_units) != 0)
        error_table_entry("MeshCutoff UNITS corrupt : using default\n");
      }
    else
      error_table_entry("MeshCutoff line corrupt : using defaults\n");
    }

  if (g_ascii_strncasecmp("Harris_functional", *buff, 17) == 0)
    {
    if (num_tokens >=2)         //Harris_functional boolean #crap
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.harris_functional = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.harris_functional = TRUE;
      else
        model->siesta.harris_functional = FALSE;
      }
    else
      error_table_entry("Harris_functional line corrupt\n");
    }

  if (g_ascii_strncasecmp("ElectronicTemperature", *buff, 21) == 0)
    {
    if (num_tokens > 2)             //temp real units #othercrap
      {
      model->siesta.electronic_temperature = str_to_float(*(buff+1));
      if (set_temperature_units(*(buff+2), &model->siesta.electronic_temperature_units) != 0)
        error_table_entry("ElectronicTemperature UNITS corrupt : using default\n");
      }
    else if (num_tokens == 2)
      {
      model->siesta.electronic_temperature = str_to_float(*(buff+1));
//default temp units? KELVIN FOR NOW!
      model->siesta.electronic_temperature_units = K_TEMP;
      error_table_entry("Units missing ElectronicTemperature : using Kelvin\n");
      }
    else
      error_table_entry("ElectronicTemperature line corrupt : using defaults\n");
    }

//spin_polarised -> SpinPolarized    true||.true.
  if (g_ascii_strncasecmp("SpinPolarized", *buff, 13) == 0)
    {
    if (num_tokens >= 2)             //SpinPolarized boolean #othercrap
      {
      if (g_ascii_strncasecmp("true", *(buff+1), 4) == 0)
        model->siesta.spin_polarised = TRUE;
      else if (g_ascii_strncasecmp(".true", *(buff+1), 5) == 0)
        model->siesta.spin_polarised = TRUE;
      else
        model->siesta.spin_polarised = FALSE;
      }
    else
      error_table_entry("SpinPolarized line corrupt : using defaults\n");
    }

//SIESTA - SCF OPTIONS
// -----> right no_of_cycles -> MaxSCFIterations

  if (g_ascii_strncasecmp("MaxSCFIterations", *buff, 16) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.no_of_cycles = str_to_float(*(buff+1));
    else
      error_table_entry("MaxSCFIterations line corrupt : using defaults\n");
    }

  if (g_ascii_strncasecmp("DM.NumberPulay", *buff, 14) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.no_of_pulay_matrices = str_to_float(*(buff+1));
    else
      error_table_entry("DM.NumberPulay line corrupt : using defaults\n");
    }

//mixing_weight -> DM.MixingWeight float #comments
  if (g_ascii_strncasecmp("DM.MixingWeight", *buff, 15) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.mixing_weight = str_to_float(*(buff+1));
    else
      error_table_entry("DM.MixingWeight line corrupt : using defaults\n");
    }

//run_type - enum { SINGLE_POINT, OPTIMISATION, MOLECULAR_DYNAMICS, PHONON_CALCULATION };
  if (g_ascii_strncasecmp("MD.TypeOfRun", *buff, 12) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("CG", *(buff+1), 2) == 0)
        {
//soo is it a single point or an optimisation?
//have to check for number of steps
        if (model->siesta.number_of_steps == 0)
          model->siesta.run_type = SINGLE_POINT;
        else
          model->siesta.run_type = OPTIMISATION;
        }
      else if (g_ascii_strncasecmp("Verlet", *(buff+1), 6) == 0)
        {
        model->siesta.run_type = MOLECULAR_DYNAMICS;
        model->siesta.md_type_of_run = VERLET_MDRUN;
        }
      else if (g_ascii_strncasecmp("Nose", *(buff+1), 4) == 0)
        {
        model->siesta.run_type = MOLECULAR_DYNAMICS;
        model->siesta.md_type_of_run = NOSE_MDRUN;
        }
      else if (g_ascii_strncasecmp("ParrinelloRahman", *(buff+1), 16) == 0)
        {
        model->siesta.run_type = MOLECULAR_DYNAMICS;
        model->siesta.md_type_of_run = PARRINELLOPAHMAN_MDRUN;
        }
      else if (g_ascii_strncasecmp("NoseParrinelloRahman", *(buff+1), 20) == 0)
        {
        model->siesta.run_type = MOLECULAR_DYNAMICS;
        model->siesta.md_type_of_run = NOSEPARRINELLOPAHMAN_MDRUN;
        }
      else if (g_ascii_strncasecmp("Anneal", *(buff+1), 6) == 0)
        {
        model->siesta.run_type = MOLECULAR_DYNAMICS;
        model->siesta.md_type_of_run = ANNEAL_MDRUN;
        }
      else if (g_ascii_strncasecmp("FC", *(buff+1), 2) == 0)
        {
        model->siesta.run_type = PHONON_CALCULATION;
        model->siesta.md_type_of_run = FC_MDRUN;
        }
      else if (g_ascii_strncasecmp("Phonon", *(buff+1), 6) == 0)
        {
        model->siesta.run_type = PHONON_CALCULATION;
        model->siesta.md_type_of_run = PHONON_MDRUN;
        }
      }
    else
      error_table_entry("MD.TypeOfRun line corrupt");
    }

  //model->siesta.md_variable_cell
  if (g_ascii_strncasecmp("MD.VariableCell", *buff, 15) == 0)
    {
    if (num_tokens >=2)
      {
      if (set_true_false(*(buff+1), &model->siesta.md_variable_cell) != 0)
        error_table_entry("Failed to set MD.VariableCell\n");
      }
    else
      error_table_entry("MD.VariableCell line corrupt : using defaults\n");
    }

  //model->siesta.md_num_cg_steps
  if (g_ascii_strncasecmp("MD.NumCGsteps", *buff, 13) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.md_num_cg_steps = str_to_float(*(buff+1));
    else
      error_table_entry("MD.NumCGsteps line corrupt\n");
    }

  //model->siesta.md_max_cg_displacement
  if (g_ascii_strncasecmp("MD.MaxCGDispl", *buff, 13) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_max_cg_displacement = str_to_float(*(buff+1));
      if (set_length_units(*(buff+2), &model->siesta.md_max_cg_displacement_units) != 0)
        error_table_entry("MD.MaxCGDispl units unable to be set\n");
      }
    else
      error_table_entry("MD.MaxCGDispl line corrupt\n");
    }

  //model->siesta.md_precondition_variable_cell
  if (g_ascii_strncasecmp("MD.PreconditionVariableCell", *buff, 27) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_precondition_variable_cell = str_to_float(*(buff+1));
      if (set_length_units(*(buff+2), &model->siesta.md_precondition_variable_cell_units) != 0)
        error_table_entry("MD.PreconditionVariableCell units unable to be set\n");
      }
    else
        error_table_entry("MD.PreconditionVariableCell line corrupt\n");
    }

  //model->siesta.md_max_force_tol
  if (g_ascii_strncasecmp("MD.MaxForceTol", *buff, 14) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_max_force_tol = str_to_float(*(buff+1));
      if (set_force_units(*(buff+2), &model->siesta.md_max_force_tol_units) != 0)
        error_table_entry("MD.MaxForceTol units unable to be set\n");
      }
    else
      error_table_entry("MD.MaxForceTol line corrupt\n");
    }

  //model->siesta.md_max_stress_tol
  if (g_ascii_strncasecmp("MD.MaxStressTol", *buff, 14) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_max_stress_tol = str_to_float(*(buff+1));
      if (set_pressure_units(*(buff+2), &model->siesta.md_max_stress_tol_units) != 0)
        error_table_entry("MD.MaxStressTol units unable to be set\n");
      }
    else
      error_table_entry("MD.MaxStressTol line corrupt\n");
    }

  //model->siesta.md_inital_time_step
  if (g_ascii_strncasecmp("MD.InitialTimeStep", *buff, 18) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.md_inital_time_step = str_to_float(*(buff+1));
    else
      error_table_entry("MD.InitialTimeStep line corrupt\n");
    }

  //model->siesta.md_final_time_step
  if (g_ascii_strncasecmp("MD.FinalTimeStep", *buff, 16) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.md_final_time_step = str_to_float(*(buff+1));
    else
      error_table_entry("MD.FinalTimeStep line corrupt\n");
    }

  //model->siesta.timestep
  if (g_ascii_strncasecmp("MD.LengthTimeStep", *buff, 17) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.timestep = str_to_float(*(buff+1));
      if (set_time_units(*(buff+2), &model->siesta.timestep_units) != 0)
        error_table_entry("*ERROR* - MD.LengthTimeStep units corrupt\n");
      }
    else if (num_tokens == 2)
      {
      error_table_entry("MD.LengthTimeStep units MISSING\n");
      model->siesta.timestep = str_to_float(*(buff+1));
      set_time_units("", &model->siesta.timestep_units);
      }
    }

  //model->siesta.md_inital_temperature
  if (g_ascii_strncasecmp("MD.InitialTemperature", *buff, 21) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_inital_temperature = str_to_float(*(buff+1));
      if (set_temperature_units(*(buff+2), &model->siesta.md_inital_temperature_units) != 0)
        error_table_entry("MD.InitialTemperature units corrupt\n");
      }
    else
      error_table_entry("MD.InitialTemperature line corrupt\n");
    }

  //model->siesta.md_target_temperature
  if (g_ascii_strncasecmp("MD.TargetTemperature", *buff, 20) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_target_temperature = str_to_float(*(buff+1));
      if (set_temperature_units(*(buff+2), &model->siesta.md_target_temperature_units) != 0)
        error_table_entry("MD.TargetTemperature units corrupt\n");
      }
    else
      error_table_entry("MD.TargetTemperature line corrupt\n");
    }

  //model->siesta.md_quench
  if (g_ascii_strncasecmp("MD.Quench", *buff, 9) == 0)
    {
    if (num_tokens >= 2)
      {
      if (set_true_false(*(buff+1), &model->siesta.md_quench) != 0)
        error_table_entry("MD.Quench boolean corrupt\n");
      }
    else
     error_table_entry("MD.Quench line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.TargetPressure", *buff, 20) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_target_pressure = str_to_float(*(buff+1));
      if (set_pressure_units(*(buff+2), &model->siesta.md_target_pressure_units) != 0)
        error_table_entry("MD.TargetPressure units corrupt\n");
      }
    else
      error_table_entry("MD.TargetPressure line corrupt\n");
    }

#if OLD_STYLE
  if (g_ascii_strncasecmp("\%block", *buff, 6) == 0 && num_tokens > 1)
  {
    if  (g_ascii_strncasecmp("MD.TargetStress", *(buff+1), 15) == 0)
    {
      for (;;)
      {
        g_strfreev(buff);
        buff = get_tokenized_line(fp, &num_tokens);
        if (!buff)
           break;
        if (g_ascii_strncasecmp("\%endblock", *buff, 9) == 0)
          break;

        if (num_tokens == 6)
        {
            model->siesta.md_target_stress_xx = str_to_float(*(buff));
            model->siesta.md_target_stress_yy = str_to_float(*(buff+1));
            model->siesta.md_target_stress_zz = str_to_float(*(buff+2));
            model->siesta.md_target_stress_xy = str_to_float(*(buff+3));
            model->siesta.md_target_stress_xz = str_to_float(*(buff+4));
            model->siesta.md_target_stress_yz = str_to_float(*(buff+5));
        }
        else
        {
            //invalid stress block
            text = error_table_entry("*ERROR* - MD.TargetStress block corrupt\n");
            gui_text_show(ERROR, text);
            g_free(text);
        }

      }
    }
  }
#endif

  if (g_ascii_strncasecmp("MD.NoseMass", *buff, 11) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_nose_mass = str_to_float(*(buff+1));
      if (set_mominert_units(*(buff+2), &model->siesta.md_nose_mass_units) != 0)
        error_table_entry("*ERROR* - MD.NoseMass units corrupt\n");
      }
    else
      error_table_entry("*ERROR* - MD.NoseMass line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.ParrinelloRahmanMass", *buff, 23) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_parrinello_rahman_mass = str_to_float(*(buff+1));
      if (set_mominert_units(*(buff+2), &model->siesta.md_parrinello_rahman_mass_units) != 0)
        error_table_entry("MD.ParrinelloRahmanMass units corrupt\n");
      }
    else
      error_table_entry("MD.ParrinelloRahmanMass line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.AnnealOption", *buff, 23) == 0)
    {
    if (num_tokens >= 2)
      {
      if (g_ascii_strncasecmp("Temperature", *(buff+1), 11) == 0)
        model->siesta.md_anneal_option = TEMPERATURE_ANNEAL;
      else if (g_ascii_strncasecmp("Pressure", *(buff+1), 8) == 0)
        model->siesta.md_anneal_option = PRESSURE_ANNEAL;
      else if (g_ascii_strncasecmp("TemperatureandPressure", *(buff+1), 22) == 0)
        model->siesta.md_anneal_option = TEMP_AND_PRESS_ANNEAL;
      else
       error_table_entry("MD.AnnealOption string corrupt\n");
      }
    else
      error_table_entry("MD.AnnealOption line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.TauRelax", *buff, 11) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_tau_relax = str_to_float(*(buff+1));
      if (set_time_units(*(buff+2), &model->siesta.md_tau_relax_units) != 0)
        error_table_entry("MD.TauRelax units corrupt\n");
      }
    else
      error_table_entry("MD.TauRelax line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.BulkModulus", *buff, 14) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_bulk_modulus = str_to_float(*(buff+1));
      if (set_pressure_units(*(buff+2), &model->siesta.md_bulk_modulus_units) != 0)
        error_table_entry("MD.BulkModulus units corrupt\n");
      }
    else
      error_table_entry("MD.BulkModulus line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.FCDispl", *buff, 10) == 0)
    {
    if (num_tokens >= 3)
      {
      model->siesta.md_fc_displ = str_to_float(*(buff+1));
      if (set_length_units(*(buff+2), &model->siesta.md_fc_displ_units) != 0)
        error_table_entry("MD.FCDispl units corrupt\n");
      }
    else
      error_table_entry("MD.FCDispl line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.FCfirst", *buff, 10) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.md_fc_first = str_to_float(*(buff+1));
    else
      error_table_entry("MD.FCfirst line corrupt\n");
    }

  if (g_ascii_strncasecmp("MD.FClast", *buff, 9) == 0)
    {
    if (num_tokens >= 2)
      model->siesta.md_fc_last = str_to_float(*(buff+1));
    else
      error_table_entry("MD.FClast line corrupt\n");
    }

/* zmatrix */
  if (g_ascii_strncasecmp("zm.unitslength", *buff, 14) == 0)
    {
    if (num_tokens)
      {
      if (g_ascii_strncasecmp("ang", *(buff+1), 3) == 0)
        zmat_distance_units_set(model->zmatrix, ANGSTROM);
      else
        zmat_distance_units_set(model->zmatrix, BOHR);
      }
    }
  if (g_ascii_strncasecmp("zm.unitsangle", *buff, 13) == 0)
    {
    if (num_tokens)
      {
      if (g_ascii_strncasecmp("deg", *(buff+1), 3) == 0)
        zmat_angle_units_set(model->zmatrix, DEGREES); 
      else
        zmat_angle_units_set(model->zmatrix, RADIANS); 
      }
    }

#if OLD_STYLE
  if (read_inital_cooords)
  {
  if (g_ascii_strncasecmp("AtomicCoordinates", *(buff+1), 17) == 0)
    {
    }
  }
#endif
    
    
    // exit condition - "************************** End of input data file"
    if (g_ascii_strncasecmp("**************************", *buff, 26) == 0)
    {
        if (g_ascii_strncasecmp("End", *(buff+1), 3) == 0)
        {
            //leave routine -> break the for loop
            g_strfreev(buff);
            free_slist(species_list);        
            return 1;
        }
    }

/* loop cleanup */
  siesta_done_line:;

  g_strfreev(buff);
  g_free(line);
  }

siesta_done_file:;


/* NEW - process zmatrix cores */
zmat_type(model->zmatrix, species_list);
zmat_process(model->zmatrix, model);


/* free the species list */
free_slist(species_list);

  
return 0;
}

/**************************/
/* fdf input file reading */
/**************************/
#define DEBUG_READ_FDF 0
gint read_fdf(gchar *filename, struct model_pak *model)
{
gint i;
gchar *text;

FILE *fp;

/* checks */
g_return_val_if_fail(model != NULL, 1);
g_return_val_if_fail(filename != NULL, 2);

fp = fopen(filename, "rt");
if (!fp)
  return(3);

error_table_clear();

/* terry's mod */
read_fdf_block(fp, model, TRUE);

/* check cores */
model->cores = g_slist_reverse(model->cores);
i = g_slist_length(model->cores);
if (model->siesta.num_atoms != i)
  {
  text = g_strdup_printf("Inconsistancy reading %s: expected %d cores, but found %d.\n",
                          filename, model->siesta.num_atoms, i);
  gui_text_show(ERROR, text);
  g_free(text);
  }

/* model setup */
strcpy(model->filename, filename);

model_prep(model);

/* NB: this relies on having a valid latmat */
seista_density_files(filename, model);

error_table_print_all();

return(0);
}

/*******************************************/
/* read single SIESTA output configuration */
/*******************************************/
#define DEBUG_READ_SOUT 0
gint read_sout_block(FILE *fp, struct model_pak *model)
{
gint i, num_tokens;
gchar **temp, **buff, line[LINELEN];
GString *title, *energy_string, *grad_string, *pressure_string;
GSList *clist;
struct core_pak *core;

clist = model->cores;

/* get 1st line of coords */
if (fgetline(fp, line))
  return(1);
buff = tokenize(line, &num_tokens);

while (num_tokens > 4)
  {
  if (clist)
    {
    core = clist->data;
    clist = g_slist_next(clist);
    }
  else
    {
    core = new_core(*(buff+4), model);
    model->cores = g_slist_append(model->cores, core);
    }

  core->x[0] = str_to_float(*(buff+0));
  core->x[1] = str_to_float(*(buff+1));
  core->x[2] = str_to_float(*(buff+2));

/* get next line */
  g_strfreev(buff);
  if (fgetline(fp, line))
    return(2);
  buff = tokenize(line, &num_tokens);
  }
g_strfreev(buff);

/* attempt to get the lattice matrix */
/* if not found, use the initial values */
/* removed the following line as if initial structure used LatticeVectors, this stopped the pbcs being constructed */
/* model->construct_pbc = FALSE;*/
while (!fgetline(fp, line))
  {
/* if next frame encoutered - assume there is no outcell data & exit */
  if (g_ascii_strncasecmp(line, "siesta:        Begin CG move", 28) == 0)
    break;
  /* new version of siesta doesn't have the siesta: */
  if (g_ascii_strncasecmp(line, "                            Begin CG move", 41) == 0)
    break;
  /* Read in MD steps from siesta: */
  if (g_ascii_strncasecmp(line, "                            Begin MD step", 41) == 0)
    break;

/* acquire cell lengths */
  if (g_ascii_strncasecmp(line, "outcell: Cell vector modules", 28) == 0)
    {
    model->construct_pbc = FALSE;
    temp = g_strsplit(line, ":", 3);
    buff = tokenize(*(temp+2), &num_tokens);
    g_strfreev(temp);

    if (num_tokens > 2)
      {
      model->pbc[0] = str_to_float(*(buff+0));
      model->pbc[1] = str_to_float(*(buff+1));
      model->pbc[2] = str_to_float(*(buff+2));
      }
    else
      printf("Unexpected data format reading cell lengths.\n");
    g_strfreev(buff);
    }

/* acquire cell angles */
  if (g_ascii_strncasecmp(line, "outcell: Cell angles", 20) == 0)
    {
    temp = g_strsplit(line, ":", 3);
    buff = tokenize(*(temp+2), &num_tokens);
    g_strfreev(temp);

    if (num_tokens > 2)
      {
/* get the angles (in radians) */
      model->pbc[3] = D2R*str_to_float(*(buff+0));
      model->pbc[4] = D2R*str_to_float(*(buff+1));
      model->pbc[5] = D2R*str_to_float(*(buff+2));
      }
    else
      printf("Unexpected data format reading cell angles.\n");
    g_strfreev(buff);

/* no more data - break out */
/*
    break;
*/
    }

  if (g_ascii_strncasecmp(line, "siesta: E_KS(eV)", 16) == 0)
    {
    buff = tokenize(line, &num_tokens);
    model->siesta.energy = str_to_float(*(buff+3));
    model->siesta.have_energy = TRUE;
    g_strfreev(buff);
    }

  if (g_strrstr(line, "constrained") != NULL)
    {
    buff = tokenize(line, &num_tokens);
    model->siesta.max_grad = str_to_float(*(buff+1));
    model->siesta.have_max_grad = TRUE;
    g_strfreev(buff);
    }

  if (g_ascii_strncasecmp(line, "siesta: Pressure (static)", 25) == 0)
    {
    for (i=0; i<4; i++)
      {
      if (fgetline(fp, line))
        return(2);
      }
    buff = tokenize(line, &num_tokens);
    pressure_string = g_string_new("");
    g_string_append_printf(pressure_string, "%.4f %s", str_to_float(*(buff+1)), *(buff+3));
    property_add_ranked(5, "Final Pressure", pressure_string->str, model);
    g_string_free(pressure_string, TRUE);
    }
  }

g_free(model->title);

/* TODO read in units from sot file */
title = g_string_new("");
if (model->siesta.have_energy)
  {
  energy_string = g_string_new("");
  g_string_append_printf(energy_string, "%.5f eV", model->siesta.energy);
  property_add_ranked(3, "Energy", energy_string->str, model);
  g_string_free(energy_string, TRUE);
  g_string_append_printf(title, "E");
  g_string_append_printf(title, " = %.4f eV, ", model->siesta.energy);
  }
if (model->siesta.have_max_grad)
  {
  grad_string = g_string_new("");
  g_string_append_printf(grad_string, "%.4f eV/A", model->siesta.max_grad);
  property_add_ranked(4, "Maximum Gradient", grad_string->str, model);
  g_string_free(grad_string, TRUE);
  g_string_append_printf(title, "max grad = %.5f", model->siesta.max_grad);
  }
model->title = g_strdup(title->str);
g_string_free(title, TRUE);

return(0);
}

/*******************************/
/* SIESTA output frame reading */
/*******************************/
gint read_sout_frame(FILE *fp, struct model_pak *model)
{
    return(read_sout_block(fp, model));
}

/********************************/
/* Read in a SIESTA output file */
/********************************/
/* could almost use the read_fdf() code, as SIESTA spits out a copy */
/* however, we do need to know the number of frames for animation... */
gint read_sout(gchar *filename, struct model_pak *model)
{
gint frame=0;
gchar **buff, line[LINELEN], *text;
FILE *fp;
    
gint num_tokens, i;
gdouble scale = 1.0;

fp = fopen(filename, "rt");
if (!fp)
  return(1);

fgetline(fp, line);
while (g_ascii_strncasecmp("************************** Dump of input data file", line, 50) != 0)
    fgetline(fp, line);

//should be at the input file params -- dont want inital co-ords.
//read_fdf_block(fp,model, TRUE);
//should magicly return at the ======


while (!fgetline(fp, line))
  {
//---------------START TERRY COMMENT DUE TO read_fdf_pak_filler()----------      

/* lattice constant scaling */
  if (g_ascii_strncasecmp("LatticeConstant", line, 15) == 0)
    {
    scale = siesta_scale_parse(line);
    continue;
    }

/* default cell dimensions */
  if (g_ascii_strncasecmp("\%block LatticeParameters", line, 24) == 0)
  {
     if (fgetline(fp, line))
     return(2);
     buff = tokenize(line, &num_tokens);
     if (num_tokens > 5)
      {
      model->pbc[0] = scale*str_to_float(*(buff+0));
      model->pbc[1] = scale*str_to_float(*(buff+1));
      model->pbc[2] = scale*str_to_float(*(buff+2));
      model->pbc[3] = D2R*str_to_float(*(buff+3));
      model->pbc[4] = D2R*str_to_float(*(buff+4));
      model->pbc[5] = D2R*str_to_float(*(buff+5));
      model->construct_pbc = FALSE;
      model->periodic = 3;
      }
    g_strfreev(buff);
  }
  else if (g_ascii_strncasecmp("\%block LatticeVectors", line, 20) == 0)
  {
      for (i=0; i<3; i++)
      {
        if (fgetline(fp, line))
          return(2);
        buff = tokenize(line, &num_tokens);
        if (num_tokens == 3)
        {
          model->latmat[0+i] = scale*str_to_float(*(buff+0));
          model->latmat[3+i] = scale*str_to_float(*(buff+1));
          model->latmat[6+i] = scale*str_to_float(*(buff+2));
        }
        g_strfreev(buff);
        model->construct_pbc = TRUE;
        model->periodic = 3;
      }
  }

//---------------END TERRY COMMENT DUE TO read_fdf_pak_filler()----------     
 

/* functional */
  if (g_ascii_strncasecmp(line, "xc_check: Exchange-correlation functional:", 42) == 0)
    {
    if (fgetline(fp, line))
      return(2);
    if (g_ascii_strncasecmp(line, "xc_check: GGA Perdew, Burke & Ernzerhof 1996", 44) == 0)
      property_add_ranked(7, "Functional", "PBE", model);
    }

/* k-points */
  if (g_ascii_strncasecmp(line, "siesta: k-grid: Number of k-points", 34) == 0)
    {
    buff = g_strsplit(line, "=", 2);
    g_strchug(g_strchomp(*(buff+1)));
    property_add_ranked(10, "K-points", *(buff+1), model);
    g_strfreev(buff);
    }

/* Mesh cutoff */
  if (g_ascii_strncasecmp(line, "redata: Mesh Cutoff", 19) == 0)
  {
    buff = g_strsplit(line, "=", 2);
    text = format_value_and_units(*(buff+1), 2);
    property_add_ranked(6, "Mesh Cutoff", text, model);
    g_free(text);
    g_strfreev(buff);
  }

/* Energy Shift */
  if (g_ascii_strncasecmp(line, "SPLIT: energy shift",  19) == 0)
  {
    buff = g_strsplit(line, "=", 2);
    text = format_value_and_units(*(buff+1), 6);
    property_add_ranked(6, "Energy Shift", text, model);
    g_free(text);
    g_strfreev(buff);
  }

/* coordinates */
  if (model->siesta.md_type_of_run != FC_MDRUN)
  {
      if (g_ascii_strncasecmp(line, "outcoor: Atomic coordinates", 27) == 0)
      {
        if (g_strrstr(line, "Ang") != NULL)
          model->fractional = FALSE;
        else if (g_strrstr(line, "fractional") != NULL)
          model->fractional = TRUE;
        else if (g_strrstr(line, "Bohr") != NULL)
          model->coord_units = BOHR;
    
        else
          {
          gui_text_show(ERROR, "unexpected coordinate type\n");
          return(2);
          }
        add_frame_offset(fp, model);
        read_sout_block(fp, model);
        frame++;
      }
  }


/* seems to increment the number of frames but doesn't set the animation pointer! */
/* line only occurs at end of a run that runs out of cycles so commenting out! */
/*  if (g_ascii_strncasecmp(line, "outcoor: Final", 14) == 0)
    frame++;*/
  }
  
  
  if (model->siesta.md_type_of_run == FC_MDRUN)
  {
    //something went wrong
    error_table_entry("MD.TypeOfRun == FC -> so inital co-ords from input shown\n");
  }
  
  

/* done */
strcpy(model->filename, filename);
g_free(model->basename);
model->basename = strdup_basename(filename);
model->num_frames = model->cur_frame = frame;
model->cur_frame--;

model_prep(model);

return(0);
}

gint set_energy_units(gchar * buff, enum siesta_energy_measurement_type * siesta_unit_type)
{
    gint allok = 0;
    if (g_ascii_strncasecmp("J", buff, 1) == 0)
        *siesta_unit_type = J_ENRG;
    else if (g_ascii_strncasecmp("erg", buff, 3) == 0)
        *siesta_unit_type = ERG_ENRG;
    else if (g_ascii_strncasecmp("eV", buff, 2) == 0)
        *siesta_unit_type = EV_ENRG;
    else if (g_ascii_strncasecmp("meV", buff, 3) == 0)
        *siesta_unit_type = MEV_ENRG;
    else if (g_ascii_strncasecmp("Ry", buff, 2) == 0)
        *siesta_unit_type = RY_ENRG;
    else if (g_ascii_strncasecmp("mRy", buff, 3) == 0)
        *siesta_unit_type = MRY_ENRG;
    else if (g_ascii_strncasecmp("Hartree", buff, 7) == 0)
        *siesta_unit_type = HARTREE_ENRG;
    else if (g_ascii_strncasecmp("K", buff, 1) == 0)
        *siesta_unit_type = KO_ENRG;
    else if (g_ascii_strncasecmp("kcal/mol", buff, 8) == 0)
        *siesta_unit_type = KCALMOLE_ENRG;
    else if (g_ascii_strncasecmp("mHartree", buff, 8) == 0)
        *siesta_unit_type = MHARTREE_ENRG;
    else if (g_ascii_strncasecmp("kJ/mol", buff, 6) == 0)
        *siesta_unit_type = KJMOL_ENRG;
    else if (g_ascii_strncasecmp("Hz", buff, 3) == 0)
        *siesta_unit_type = HZ_ENRG;
    else if (g_ascii_strncasecmp("THz", buff, 3) == 0)
        *siesta_unit_type = THZ_ENRG;
    else if (g_ascii_strncasecmp("cm-1", buff, 4) == 0)
        *siesta_unit_type = CM_ENRG;
    else if (g_ascii_strncasecmp("cm**-1", buff, 6) == 0)
        *siesta_unit_type = CMCM_ENRG;
    else
    {
        //no match - set default
        *siesta_unit_type = RY_ENRG;
        allok = 1;
    }
    return allok;
}
gint set_temperature_units(gchar * buff, enum siesta_temperature_measurement_type * siesta_unit_type)
{
    gint allok = 0;
    if (g_ascii_strncasecmp("K", buff, 1) == 0)
        *siesta_unit_type = K_TEMP;
    else if (g_ascii_strncasecmp("C", buff, 1) == 0)
        *siesta_unit_type = C_TEMP;
    else
    {
        //no match - set default
        *siesta_unit_type = K_TEMP;
        allok = 1;
    }
    return allok;
}

gint set_mass_units(gchar * buff, enum siesta_temperature_measurement_type * siesta_unit_type)
{
    //KG_MASS, G_MASS, AMU_MASS
    //
    gint allok = 0;
    if (g_ascii_strncasecmp("Kg", buff, 2) == 0)
        *siesta_unit_type = KG_MASS;
    else if (g_ascii_strncasecmp("g", buff, 1) == 0)
        *siesta_unit_type = G_MASS;
    else if (g_ascii_strncasecmp("amu", buff, 3) == 0)
        *siesta_unit_type = AMU_MASS;
    else
    {
        //no match - set default
        *siesta_unit_type = AMU_MASS;
        allok = 1;
    }
    return allok;
}

gint set_length_units(gchar * buff, enum siesta_length_measurement_type * siesta_unit_type)
{
    //M_LEN, CM_LEN, NM_LEN, ANG_LEN, BOHR_LEN
    gint allok = 0;
    if (g_ascii_strncasecmp("m", buff, 1) == 0)
        *siesta_unit_type = M_LEN;
    else if (g_ascii_strncasecmp("cm", buff, 2) == 0)
        *siesta_unit_type = CM_LEN;
    else if (g_ascii_strncasecmp("nm", buff, 2) == 0)
        *siesta_unit_type = NM_LEN;
    else if (g_ascii_strncasecmp("Ang", buff, 3) == 0)
        *siesta_unit_type = ANG_LEN;
    else if (g_ascii_strncasecmp("Bohr", buff, 4) == 0)
        *siesta_unit_type = BOHR_LEN;
    else
    {
        //no match - set default
        *siesta_unit_type = BOHR_LEN;
        allok = 1;
    }
    return allok;
}

gint set_time_units(gchar * buff, enum siesta_time_measurement_type * siesta_unit_type)
{
    //S_TIME, FS_TIME, PS_TIME, NS_TIME
    gint allok = 0;
    if (g_ascii_strncasecmp("S", buff, 1) == 0)
        *siesta_unit_type = S_TIME;
    else if (g_ascii_strncasecmp("fs", buff, 2) == 0)
        *siesta_unit_type = FS_TIME;
    else if (g_ascii_strncasecmp("ps", buff, 2) == 0)
        *siesta_unit_type = PS_TIME;
    else if (g_ascii_strncasecmp("ns", buff, 2) == 0)
        *siesta_unit_type = NS_TIME;
    else
    {
        //no match - set default
        *siesta_unit_type = FS_TIME;
        allok = 1;
    }
    return allok;
}

gint set_pressure_units(gchar * buff, enum siesta_pressure_measurement_type * siesta_unit_type)
{
    // P_PRES, MPA_PRES, GPA_PRES, ATM_PRES, BAR_PRES, MBAR_PRES, RYBOHR3_PRES, EVANG3_PRES
    gint allok = 0;
    if (g_ascii_strncasecmp("Pa", buff, 2) == 0)
        *siesta_unit_type = P_PRES;
    else if (g_ascii_strncasecmp("MPa", buff, 3) == 0)
        *siesta_unit_type = MPA_PRES;
    else if (g_ascii_strncasecmp("GPa", buff, 3) == 0)
        *siesta_unit_type = GPA_PRES;
    else if (g_ascii_strncasecmp("atm", buff, 3) == 0)
        *siesta_unit_type = ATM_PRES;
    else if (g_ascii_strncasecmp("bar", buff, 3) == 0)
        *siesta_unit_type = BAR_PRES;
    else if (g_ascii_strncasecmp("Mbar", buff, 4) == 0)
        *siesta_unit_type = MBAR_PRES;
    else if (g_ascii_strncasecmp("Ry/Bohr**3", buff, 10) == 0)
        *siesta_unit_type = RYBOHR3_PRES;
    else if (g_ascii_strncasecmp("eV/Ang**3", buff, 9) == 0)
        *siesta_unit_type = EVANG3_PRES;
    else
    {
        //no match - set default
        *siesta_unit_type = P_PRES;
        allok = 1;
    }
    return allok;
}

gint set_force_units(gchar * buff, enum siesta_force_measurement_type * siesta_unit_type)
{
    //N_FORCE, EVANG_FORCE, RYBOHR_FORCE
    gint allok = 0;
    if (g_ascii_strncasecmp("N", buff, 1) == 0)
        *siesta_unit_type = N_FORCE;
    else if (g_ascii_strncasecmp("eV/Ang", buff, 6) == 0)
        *siesta_unit_type = EVANG_FORCE;
    else if (g_ascii_strncasecmp("Ry/Bohr", buff, 7) == 0)
        *siesta_unit_type = RYBOHR_FORCE;
    else
    {
        //no match - set default
        *siesta_unit_type = N_FORCE;
        allok = 1;
    }
    return allok;
}

gint set_mominert_units(gchar * buff, enum siesta_mominert_measurement_type * siesta_unit_type)
{
    //KGM_MOMINERT, RYFS_MOMINERT
    gint allok = 0;
    if (g_ascii_strncasecmp("Kg*m**2", buff, 7) == 0)
        *siesta_unit_type = KGM_MOMINERT;
    else if (g_ascii_strncasecmp("Ry*fs**2", buff, 8) == 0)
        *siesta_unit_type = RYFS_MOMINERT;
    else
    {
        //no match - set default
        *siesta_unit_type = KGM_MOMINERT;
        allok = 1;
    }
    return allok;
}


gint set_true_false(gchar * buff, gboolean * siesta_boolean)
{
    gint allok = 0;
    if (g_ascii_strncasecmp("true", buff, 4) == 0)
        *siesta_boolean = TRUE;
    else if (g_ascii_strncasecmp(".true", buff, 5) == 0)
        *siesta_boolean = TRUE;
    else if (g_ascii_strncasecmp(".false", buff, 6) == 0)
        *siesta_boolean = FALSE;
    else if (g_ascii_strncasecmp("false", buff, 5) == 0)
        *siesta_boolean = FALSE;
    else {
        //no match - set default
        *siesta_boolean = FALSE;
        allok = 1;
    }
    return allok;
}



gint print_file_energy_units(FILE * fp, enum siesta_energy_measurement_type units)
{
    gint allok = 0;
    switch (units)
    {
        case J_ENRG:            fprintf(fp, " J\n"); break;
        case ERG_ENRG:          fprintf(fp, " erg\n"); break;
        case EV_ENRG:           fprintf(fp, " eV\n"); break;
        case MEV_ENRG:          fprintf(fp, " meV\n"); break;
        case RY_ENRG:           fprintf(fp, " Ry\n"); break;
        case MRY_ENRG:          fprintf(fp, " mRy\n"); break;
        case HARTREE_ENRG:      fprintf(fp, " Hartree\n"); break;
        case KO_ENRG:           fprintf(fp, " K\n"); break;
        case KCALMOLE_ENRG:     fprintf(fp, " kcal/mol\n"); break;
        case MHARTREE_ENRG:     fprintf(fp, " mHartree\n"); break;
        case KJMOL_ENRG:        fprintf(fp, " kJ/mol\n"); break;
        case HZ_ENRG:           fprintf(fp, " Hz\n"); break;
        case THZ_ENRG:          fprintf(fp, " THz\n"); break;
        case CM_ENRG:           fprintf(fp, " cm-1\n"); break;
        case CMCM_ENRG:         fprintf(fp, " cm**-1\n"); break;
        default:
            //no units given?
            //print new line
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_mass_units(FILE * fp, enum siesta_mass_measurement_type units)
{
    gint allok = 0;
    switch (units)
    {
        case KG_MASS:            fprintf(fp, " Kg\n"); break;
        case G_MASS:             fprintf(fp, " g\n"); break;
        case AMU_MASS:           fprintf(fp, " amu\n"); break;
        default:
            //no units given? print new line
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_length_units(FILE * fp, enum siesta_length_measurement_type units)
{
    gint allok = 0;
    switch (units)  //M_LEN, CM_LEN, NM_LEN, ANG_LEN, BOHR_LEN
    {
        case M_LEN:             fprintf(fp, " m\n"); break;
        case CM_LEN:            fprintf(fp, " cm\n"); break;
        case NM_LEN:            fprintf(fp, " nm\n"); break;
        case ANG_LEN:           fprintf(fp, " Ang\n"); break;
        case BOHR_LEN:          fprintf(fp, " Bohr\n"); break;
        default:
            //no units given? print new line
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_time_units(FILE * fp, enum siesta_time_measurement_type units)
{
    gint allok = 0;
    switch (units)  //S_TIME, FS_TIME, PS_TIME, NS_TIME
    {
        case S_TIME:            fprintf(fp, " s\n"); break;
        case FS_TIME:           fprintf(fp, " fs\n"); break;
        case PS_TIME:           fprintf(fp, " ps\n"); break;
        case NS_TIME:           fprintf(fp, " ns\n"); break;
        default:
            //no units given? print new line or force units?
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

//Pressure
gint print_file_pressure_units(FILE * fp, enum siesta_pressure_measurement_type units)
{
    gint allok = 0;
    switch (units)
    {
        case P_PRES:                fprintf(fp, " Pa\n"); break;
        case MPA_PRES:              fprintf(fp, " MPa\n"); break;
        case GPA_PRES:              fprintf(fp, " GPa\n"); break;
        case ATM_PRES:              fprintf(fp, " atm\n"); break;
        case BAR_PRES:              fprintf(fp, " bar\n"); break;
        case MBAR_PRES:             fprintf(fp, " Mbar\n"); break;
        case RYBOHR3_PRES:          fprintf(fp, " Ry/Bohr**3\n"); break;
        case EVANG3_PRES:           fprintf(fp, " eV/Ang**3\n"); break;
        default:
            //no units given? print new line or force units?
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_temperature_units(FILE * fp, enum siesta_temperature_measurement_type units)
{
    gint allok = 0;
    switch (units)
    {
        case C_TEMP:                fprintf(fp, " C\n"); break;
        case K_TEMP:                fprintf(fp, " K\n"); break;
        default:
            //no units given? print new line or force units?
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_force_units(FILE * fp, enum siesta_force_measurement_type units)
{
    gint allok = 0;
    switch (units) //N_FORCE, EVANG_FORCE, RYBOHR_FORCE
    {
        case N_FORCE:               fprintf(fp, " N\n"); break;
        case EVANG_FORCE:           fprintf(fp, " eV/Ang\n"); break;
        case RYBOHR_FORCE:          fprintf(fp, " Ry/Bohr\n"); break;
        default:
            //no units given? print new line or force units?
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}

gint print_file_mominert_units(FILE * fp, enum siesta_mominert_measurement_type units)
{
    gint allok = 0;
    switch (units) //KGM_MOMINERT, RYFS_MOMINERT
    {
        case KGM_MOMINERT:            fprintf(fp, " Kg*m**2\n"); break;
        case RYFS_MOMINERT:           fprintf(fp, " Ry*fs**2\n"); break;
        default:
            //no units given? print new line or force units?
            fprintf(fp," \n");
            allok = 1;
            break;
    }
    return allok;
}


syntax highlighted by Code2HTML, v. 0.9.1