
// ********************************************************
// simulation logging

#define _SAMPLE_LOG_LEVEL             (1 << 0)
#define _SEGMENT_LOG_LEVEL            (1 << 1)
#define _PATH_LOG_LEVEL               (1 << 2)
#define _SIMULATION_LOG_LEVEL         (1 << 3)
#define _WARNING_LOG_LEVEL            (1 << 4)
#define _ERROR_LOG_LEVEL              (1 << 5)
#define _NO_ERROR_TERMINATE_LOG_LEVEL (1 << 6)
#define _ALL_LOG_LEVELS        _SAMPLE_LOG_LEVEL|_SEGMENT_LOG_LEVEL|_PATH_LOG_LEVEL|_SIMULATION_LOG_LEVEL|_WARNING_LOG_LEVEL|_ERROR_LOG_LEVEL|_NO_ERROR_TERMINATE_LOG_LEVEL
#define _LOG_LEVELS_BEING_LOGGED (_ALL_LOG_LEVELS)

#define real Re
#define imag Im

#include <complex>

#undef real
#undef imag


#include <stdio.h>

#define _LOG(logLevel, ...) \
  do { \
    if (logLevel & _LOG_LEVELS_BEING_LOGGED) { \
      if (logLevel & (_ERROR_LOG_LEVEL | _WARNING_LOG_LEVEL)) \
          printf("%s:%i: ", __FILE__, __LINE__); \
      printf(__VA_ARGS__); \
      fflush(stdout); \
      if (logLevel & (_ERROR_LOG_LEVEL | _NO_ERROR_TERMINATE_LOG_LEVEL)) \
        exit(logLevel == _ERROR_LOG_LEVEL); \
    } \
  } while (0)

// ********************************************************
// simulation includes

#include <xpdeint_platform.h>
#include <cmath>
#include <string>
#include <cstring>
#include <fstream>
#include <sstream>
#include <cstdlib>

#if CFG_OSAPI == CFG_OSAPI_POSIX // These are POSIX headers (i.e. non-windows)
  #include <sys/time.h>
#endif // POSIX

#ifdef __APPLE__
  #include <Availability.h>
  #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
    #define OS_OBJECT_USE_OBJC 0 // Don't make dispatch and xpc objects Objective-C objects.
    #include <IOKit/pwr_mgt/IOPMLib.h> // To disable user idle sleep on Mountain Lion
  #endif
#endif

#include <time.h>
#include <list>
#include <vector>
#include <algorithm>

#include <utility>
#include <map>

#if (CFG_COMPILER == CFG_COMPILER_MSVC)
  #define FFTW_DLL
#endif

#include <fftw3.h>
#include <sys/stat.h>
#include <sys/types.h>

#define _xmds_malloc fftw_malloc
#define xmds_free fftw_free

#define H5_USE_16_API
#include <hdf5.h>

#if !defined(HAVE_H5LEXISTS)
htri_t H5Lexists(hid_t loc_id, const char *name, hid_t lapl_id)
{
  H5E_auto_t error_func;
  void* error_client_data;
  // Squelch errors generated by H5Gget_objinfo. It will report errors when it can't find an object
  // but that's the purpose of calling it.
  H5Eget_auto(&error_func, &error_client_data);
  H5Eset_auto(NULL, NULL);
  herr_t err = H5Gget_objinfo(loc_id, name, false, NULL);
  H5Eset_auto(error_func, error_client_data);
  if (err >= 0)
    return true;
  else
    return false;
}
#endif

#define H5T_NATIVE_REAL H5T_NATIVE_DOUBLE
#if defined(HAVE_HDF5_HL)
  #include <hdf5_hl.h>
#endif


typedef long integer;
typedef double real;
typedef std::complex<real> XMDSComplexType;

#include <xpdeint.h>

#define complex XMDSComplexType

const complex i(0.0, 1.0);

using namespace std;

#if CFG_COMPILER == CFG_COMPILER_ICC
  //
  // Disable ICC's warning: label was declared but never referenced
  //
  #pragma warning ( disable : 177 )
#endif

inline void *xmds_malloc(size_t size);

// ********************************************************
// DEFINES
// ********************************************************

// ********************************************************
//   Simulation defines
#define _EPSILON 1e-6
#ifndef INFINITY
#define INFINITY HUGE_VAL
#endif

#ifndef MAX
#define MAX(a, b) \
  ({ typeof(a) _a = (a); \
     typeof(b) _b = (b); \
     _a > _b ? _a : _b; })
#endif

#ifndef MIN
#define MIN(a, b) \
   ({ typeof(a) _a = (a); \
      typeof(b) _b = (b); \
      _a < _b ? _a : _b; })
#endif


// ********************************************************
//   Geometry defines
#define _lattice_x ((int)128)
#define _min_x     ((real)-10)
#define _max_x     ((real)10)
#define _dx        ((real)((_max_x - _min_x)/_lattice_x))

#define _lattice_kx ((int)128)
#define _dkx        (2.0*M_PI/(_max_x - _min_x))
#define _min_kx     (-(_lattice_kx/2) * _dkx)
#define _max_kx     ((_lattice_kx - 1)/2 * _dkx)

// ********************************************************
//   field x defines
#define _x_ndims 1


// vector wavefunction defines
#define _x_wavefunction_ncomponents 1

// ********************************************************
//   segment 1 (RK4 fixed-step integrator) defines
// vector segment1_x_operators_operator0_result defines
#define _x_segment1_x_operators_operator0_result_ncomponents 1

// ********************************************************
//   field mg0_sampling defines
#define _mg0_sampling_ndims 1


// ********************************************************
//   field mg0_output defines
#define _mg0_output_ndims 2


#define _mg0_output_lattice_tau ((int)11)
#define _mg0_output_min_tau     (_mg0_output_tau[0])
#define _mg0_output_max_tau     (_mg0_output_tau[_mg0_output_lattice_tau-1])
#define _mg0_output_dtau        (_mg0_output_tau[_index_tau+1]-_mg0_output_tau[_index_tau])

// vector raw defines
#define _mg0_output_raw_ncomponents 1


// ********************************************************
// GLOBALS
// ********************************************************


// ********************************************************
//   Simulation globals

string gsArgsAndValues = "";
  
real tau;

// ********************************************************
//   Transform Multiplexer globals
typedef pair<ptrdiff_t, ptrdiff_t> _basis_pair;
typedef void (*transform_function)(bool, real, real* const __restrict__, real* const __restrict__, ptrdiff_t, ptrdiff_t);

// Less than operator needed by the C++ map class
struct _basis_pair_less_than
{
  bool operator()(const _basis_pair& _x, const _basis_pair& _y) const {
    return (_x.first < _y.first) || ((_x.first == _y.first) && (_x.second < _y.second));
  }
};

struct _transform_step
{
  transform_function _func;
  bool _forward;
  bool _out_of_place;
  ptrdiff_t _prefix_lattice;
  ptrdiff_t _postfix_lattice;
};

// Structure to hold the basis change information
struct _basis_transform_t
{
  vector<_transform_step> _transform_steps;
  real _multiplier;
  
  _basis_transform_t(real _multiplier_in = 1.0) : _multiplier(_multiplier_in) {}
  
  _basis_transform_t(const _basis_transform_t& _b) : _transform_steps(_b._transform_steps), _multiplier(_b._multiplier) {}
  
  void append(transform_function _func, bool _forward, bool _out_of_place, ptrdiff_t _prefix_lattice, ptrdiff_t _postfix_lattice)
  {
    _transform_steps.push_back((_transform_step){_func, _forward, _out_of_place, _prefix_lattice, _postfix_lattice});
  }
};

// Map type for holding (old_basis, new_basis) -> _basis_transform_t mappings
typedef map<_basis_pair, _basis_transform_t, _basis_pair_less_than> _basis_map;

_basis_map _x_wavefunction_basis_map;
_basis_map _x_segment1_x_operators_operator0_result_basis_map;

real *_auxiliary_array = NULL;

const char *_basis_identifiers[] = {
  /* 0 */ "(kx)",
  /* 1 */ "(x)",
};

// ********************************************************
//   'Globals' element globals


const double sigma = 2.0;

// ********************************************************
//   FFTW3 globals
const real _inverse_sqrt_2pi = 1.0 / sqrt(2.0 * M_PI); 
string _fftwWisdomPath;

// ********************************************************
//   Geometry globals
real* _x = NULL;

real* _kx = NULL;

// ********************************************************
//   field x globals
// vector wavefunction globals
size_t _x_wavefunction_alloc_size = 0;
complex* _x_wavefunction = NULL;
complex* _active_x_wavefunction = NULL;

ptrdiff_t _x_wavefunction_basis = -1;

// ********************************************************
//   segment 1 (RK4 fixed-step integrator) globals
complex* _segment1_akfield_x_wavefunction;
complex* _segment1_aifield_x_wavefunction;

// vector segment1_x_operators_operator0_result globals
size_t _x_segment1_x_operators_operator0_result_alloc_size = 0;
complex* _x_segment1_x_operators_operator0_result = NULL;
complex* _active_x_segment1_x_operators_operator0_result = NULL;

ptrdiff_t _x_segment1_x_operators_operator0_result_basis = -1;

// ********************************************************
//   field mg0_output globals
real* _mg0_output_tau = NULL;
unsigned long _mg0_output_index_tau = 0;

// vector raw globals
size_t _mg0_output_raw_alloc_size = 0;
real* _mg0_output_raw = NULL;
real* _active_mg0_output_raw = NULL;


// ********************************************************
// FUNCTION PROTOTYPES
// ********************************************************

// ********************************************************
//   Transform Multiplexer function prototypes
void _transform_0(bool _forward, real _multiplier, real* const __restrict__ _data_in, real* const __restrict__ _data_out, ptrdiff_t _prefix_lattice, ptrdiff_t _postfix_lattice);
void _transform_1(bool _forward, real _multiplier, real* const __restrict__ _data_in, real* const __restrict__ _data_out, ptrdiff_t _prefix_lattice, ptrdiff_t _postfix_lattice);

// ********************************************************
//   field x function prototypes
void _x_wavefunction_initialise();
void _x_wavefunction_basis_transform(ptrdiff_t new_basis);

// ********************************************************
//   segment 0 (Top level sequence) function prototypes
void _segment0();

// ********************************************************
//   segment 1 (RK4 fixed-step integrator) function prototypes
void _segment1();
inline void _segment1_calculate_delta_a(real _step);
inline void _segment1_ip_evolve(int _exponent);
inline void _segment1_calculate_nonconstant_ip_fields(real _step, int _exponent);

void _segment1_x_operators_evaluate_operator0();

void _x_segment1_x_operators_operator0_result_basis_transform(ptrdiff_t new_basis);

void _segment1_x_operators_evaluate_operator1(real _step);

// ********************************************************
//   output function prototypes
void _write_output();

FILE* _open_xsil_file(const char* _filename);
void _close_xsil_file(FILE*& fp);
void _write_xsil_header(FILE* fp);
void _write_xsil_footer(FILE* fp);

// ********************************************************
//   moment group 0 function prototypes
void _mg0_sample();
void _mg0_process();
void _mg0_write_out(FILE* _outfile);

// ********************************************************
//   field mg0_output function prototypes
void _mg0_output_raw_initialise();

// ********************************************************
// MAIN ROUTINE
// ********************************************************
int main(int argc, char **argv)
{
  #ifdef __APPLE__
    #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
  {
    IOPMAssertionID _powerAssertionID = 0;
    IOReturn __io_result = IOPMAssertionCreateWithDescription(
      kIOPMAssertionTypePreventUserIdleSystemSleep,
      CFSTR("XMDS simulation 'nlse_minimal' preventing user idle sleep"), // Assertion name
      NULL, // Assertion details
      NULL, // Human-readable reason
      NULL, // Localization bundle path
      (CFTimeInterval)0, // never timeout
      kIOPMAssertionTimeoutActionRelease,
      &_powerAssertionID
      );
    if (__io_result != kIOReturnSuccess) {
      _LOG(_WARNING_LOG_LEVEL, "Failed to disable user idle sleep\n");
    }
    // Note, this power assertion is automatically released when the process quits.
  }
    #endif
  #endif
  
    
    
  
  _x_wavefunction_alloc_size = MAX(_x_wavefunction_alloc_size, (_lattice_kx) * _x_wavefunction_ncomponents);
  _x_wavefunction_alloc_size = MAX(_x_wavefunction_alloc_size, (_lattice_x) * _x_wavefunction_ncomponents);
  _x_segment1_x_operators_operator0_result_alloc_size = MAX(_x_segment1_x_operators_operator0_result_alloc_size, (_lattice_kx) * _x_segment1_x_operators_operator0_result_ncomponents);
  _x_segment1_x_operators_operator0_result_alloc_size = MAX(_x_segment1_x_operators_operator0_result_alloc_size, (_lattice_x) * _x_segment1_x_operators_operator0_result_ncomponents);
  _mg0_output_raw_alloc_size = MAX(_mg0_output_raw_alloc_size, (_mg0_output_lattice_tau * _lattice_x) * _mg0_output_raw_ncomponents);
  _x = (real*) xmds_malloc(sizeof(real) * (_lattice_x+1));
  
  _kx = (real*) xmds_malloc(sizeof(real) * (_lattice_kx+1));
  
  _x_wavefunction = (complex*) xmds_malloc(sizeof(complex) * MAX(_x_wavefunction_alloc_size,1));
  _active_x_wavefunction = _x_wavefunction;
  _mg0_output_tau = (real*) xmds_malloc(sizeof(real) * (_mg0_output_lattice_tau+1));
  
  
  _mg0_output_raw = (real*) xmds_malloc(sizeof(real) * MAX(_mg0_output_raw_alloc_size,1));
  _active_mg0_output_raw = _mg0_output_raw;
  
  
  for (long _index_x = 0; _index_x < _lattice_x; _index_x++)
    _x[_index_x] = _min_x + _index_x*_dx;
  for (long _index_kx = 0; _index_kx < (_lattice_kx+1)/2; _index_kx++)
    _kx[_index_kx] = _index_kx*_dkx;
  for (long _index_kx = (_lattice_kx+1)/2; _index_kx < _lattice_kx; _index_kx++)
    _kx[_index_kx] = -(_lattice_kx - _index_kx) * _dkx;
  _active_mg0_output_raw = _mg0_output_raw;
  _mg0_output_raw_initialise();
  // load wisdom
  #if CFG_OSAPI == CFG_OSAPI_POSIX // Don't load wisdom on windows
  {
    char _hostName[256];
    gethostname(_hostName, 256);
    _hostName[255] = '\0'; // just in case
    
    string _pathToWisdom = getenv("HOME");
    _pathToWisdom += "/.xmds/wisdom/";
    
    string _wisdomFileName = _hostName;
    _wisdomFileName += ".wisdom";
    _wisdomFileName += ".fftw3";
    
    FILE *_fp = NULL;
    
    _fp = fopen(_pathToWisdom.c_str(), "r");
    if (_fp) {
      fclose(_fp);
    } else {
      int _result = mkdir((string(getenv("HOME")) + "/.xmds").c_str(), S_IRWXU);
      if (mkdir(_pathToWisdom.c_str(), S_IRWXU)) {
        // We failed to create the ~/.xmds/wisdom directory
        _LOG(_WARNING_LOG_LEVEL, "Warning: Cannot find enlightenment, the path to wisdom ~/.xmds/wisdom doesn't seem to exist and we couldn't create it.\n"
                                 "         I'll use the current path instead.\n");
        _pathToWisdom = ""; // present directory
      }
      
    }
    
    _fftwWisdomPath = _pathToWisdom + _wisdomFileName;
    
    FILE *_wisdomFile = NULL;
    if ( (_wisdomFile = fopen(_fftwWisdomPath.c_str(), "r")) != NULL) {
      _LOG(_SIMULATION_LOG_LEVEL, "Found enlightenment... (Importing wisdom)\n");
      fftw_import_wisdom_from_file(_wisdomFile);
      fclose(_wisdomFile);
    }
  }
  #endif // POSIX
  
  _basis_transform_t *_basis_transform = NULL;
  ptrdiff_t _auxiliary_array_size = 0;
  ptrdiff_t _max_vector_size = 0;
  real* _max_vector_array = NULL;
  
  if (2 * _x_wavefunction_alloc_size > _max_vector_size) {
    _max_vector_size = 2 * _x_wavefunction_alloc_size;
    _max_vector_array = reinterpret_cast<real*>(_x_wavefunction);
  }
  _basis_transform = &_x_wavefunction_basis_map[_basis_pair(0, 1)];
  _basis_transform->_multiplier = _inverse_sqrt_2pi * _dkx;
  _basis_transform->append(
    /* transform function */ _transform_0,
    /* forward? */ false,
    /* out-of-place? */ false,
    /* prefix lattice */ 1,
    /* postfix lattice*/ _x_wavefunction_ncomponents
  );
  _basis_transform->append(
    /* transform function */ _transform_1,
    /* forward? */ true,
    /* out-of-place? */ false,
    /* prefix lattice */ _lattice_x,
    /* postfix lattice*/ _x_wavefunction_ncomponents * 2
  );
  
  _basis_transform = &_x_wavefunction_basis_map[_basis_pair(1, 0)];
  _basis_transform->_multiplier = _inverse_sqrt_2pi * _dx;
  _basis_transform->append(
    /* transform function */ _transform_1,
    /* forward? */ false,
    /* out-of-place? */ false,
    /* prefix lattice */ _lattice_x,
    /* postfix lattice */ _x_wavefunction_ncomponents * 2
  );
  _basis_transform->append(
    /* transform function */ _transform_0,
    /* forward? */ true,
    /* out-of-place? */ false,
    /* prefix lattice */ 1,
    /* postfix lattice */ _x_wavefunction_ncomponents
  );
  
  if (2 * _x_segment1_x_operators_operator0_result_alloc_size > _max_vector_size) {
    _max_vector_size = 2 * _x_segment1_x_operators_operator0_result_alloc_size;
    _max_vector_array = reinterpret_cast<real*>(_x_segment1_x_operators_operator0_result);
  }
  _basis_transform = &_x_segment1_x_operators_operator0_result_basis_map[_basis_pair(0, 1)];
  _basis_transform->_multiplier = _inverse_sqrt_2pi * _dkx;
  _basis_transform->append(
    /* transform function */ _transform_0,
    /* forward? */ false,
    /* out-of-place? */ false,
    /* prefix lattice */ 1,
    /* postfix lattice*/ _x_segment1_x_operators_operator0_result_ncomponents
  );
  _basis_transform->append(
    /* transform function */ _transform_1,
    /* forward? */ true,
    /* out-of-place? */ false,
    /* prefix lattice */ _lattice_x,
    /* postfix lattice*/ _x_segment1_x_operators_operator0_result_ncomponents * 2
  );
  
  _basis_transform = &_x_segment1_x_operators_operator0_result_basis_map[_basis_pair(1, 0)];
  _basis_transform->_multiplier = _inverse_sqrt_2pi * _dx;
  _basis_transform->append(
    /* transform function */ _transform_1,
    /* forward? */ false,
    /* out-of-place? */ false,
    /* prefix lattice */ _lattice_x,
    /* postfix lattice */ _x_segment1_x_operators_operator0_result_ncomponents * 2
  );
  _basis_transform->append(
    /* transform function */ _transform_0,
    /* forward? */ true,
    /* out-of-place? */ false,
    /* prefix lattice */ 1,
    /* postfix lattice */ _x_segment1_x_operators_operator0_result_ncomponents
  );
  
  if (_auxiliary_array_size) {
    _auxiliary_array = (real*) xmds_malloc(sizeof(real) * _auxiliary_array_size);
  }
  
  bool _allocated_temporary_array = false;
  if (!_max_vector_array && _max_vector_size > 0) {
    _max_vector_array = (real*) xmds_malloc(sizeof(real) * _max_vector_size);
    _allocated_temporary_array = true;
  }
  
  // Make all geometry-dependent transformations prepare plans, etc.
  _transform_0(true, 1.0, _max_vector_array, _auxiliary_array, 1, _x_wavefunction_ncomponents);
  
  if (_allocated_temporary_array) {
    xmds_free(_max_vector_array);
  }
  
  // Get the time at which the simulation started
  timeval _tim;
  gettimeofday(&_tim, NULL);
  double _startTime = _tim.tv_sec + (_tim.tv_usec/1e6);
  
  /* Code that actually does stuff goes here */
  _segment0();
  
  
  _write_output();
  
  // Work out how long the simulation has run for
  gettimeofday(&_tim, NULL);
  double _endTime = _tim.tv_sec + (_tim.tv_usec/1e6);
  _LOG(_SIMULATION_LOG_LEVEL, "Time elapsed for simulation is: %.2f seconds\n", _endTime - _startTime);
  if (_auxiliary_array) {
    xmds_free(_auxiliary_array);
  }
  
  // Save wisdom
  #if CFG_OSAPI == CFG_OSAPI_POSIX
  {
    FILE *_wisdomFile = NULL;
    if ( (_wisdomFile = fopen(_fftwWisdomPath.c_str(), "w")) != NULL) {
      fftw_export_wisdom_to_file(_wisdomFile);
      fclose(_wisdomFile);
    }
  }
  #endif // POSIX
  
  fftw_cleanup();
  
  // Bing!
  _LOG(_SIMULATION_LOG_LEVEL, "\a");
  
  xmds_free(_x_wavefunction);
  _active_x_wavefunction = _x_wavefunction = NULL;
  
  xmds_free(_mg0_output_raw);
  _active_mg0_output_raw = _mg0_output_raw = NULL;
  
  
  return 0;
}

// ********************************************************
// FUNCTION IMPLEMENTATIONS
// ********************************************************

inline void *xmds_malloc(size_t size)
{
  void *retPointer = _xmds_malloc(size);
  if ( !retPointer )
    _LOG(_ERROR_LOG_LEVEL, "ERROR: Couldn't allocate %zu bytes of memory!", size);
  return retPointer;
}


// ********************************************************
//   Transform Multiplexer function implementations
// x <---> kx transform
void _transform_0(bool _forward, real _multiplier, real* const __restrict__ _data_in, real* const __restrict__ _data_out, ptrdiff_t _prefix_lattice, ptrdiff_t _postfix_lattice)
{
  if (_prefix_lattice <= 0 || _postfix_lattice <= 0) return;
  // _prefix_lattice should be 1
  // _postfix_lattice should be 1
  static fftw_plan _fftw_forward_plan = NULL;
  static fftw_plan _fftw_backward_plan = NULL;
  
  if (!_fftw_forward_plan) {
    _LOG(_SIMULATION_LOG_LEVEL, "Planning for x <---> kx transform...");
    
    fftw_iodim _transform_sizes[1], _loop_sizes[2];
    fftw_iodim *_iodim_ptr = NULL;
    
    int _transform_sizes_index = 0, _loop_sizes_index = 0;
    
    if (_prefix_lattice > 1) {
      _iodim_ptr = &_loop_sizes[_loop_sizes_index++];
      _iodim_ptr->n = _prefix_lattice;
      _iodim_ptr->is = _iodim_ptr->os = _postfix_lattice * _lattice_x;
    }
    if (_postfix_lattice > 1) {
      _iodim_ptr = &_loop_sizes[_loop_sizes_index++];
      _iodim_ptr->n = _postfix_lattice;
      _iodim_ptr->is = _iodim_ptr->os = 1;
    }
    _iodim_ptr = &_transform_sizes[_transform_sizes_index++];
    _iodim_ptr->n = _lattice_x;
    _iodim_ptr->is = _iodim_ptr->os = _postfix_lattice;
    
    
    _fftw_forward_plan = fftw_plan_guru_dft(
      _transform_sizes_index, _transform_sizes,
      _loop_sizes_index, _loop_sizes,
      reinterpret_cast<fftw_complex*>(_data_in), reinterpret_cast<fftw_complex*>(_data_in),
      FFTW_FORWARD, FFTW_PATIENT
    );
    if (!_fftw_forward_plan)
      _LOG(_ERROR_LOG_LEVEL, "(%s: %i) Unable to create forward dft plan.\n", __FILE__, __LINE__);
    
    _fftw_backward_plan = fftw_plan_guru_dft(
      _transform_sizes_index, _transform_sizes,
      _loop_sizes_index, _loop_sizes,
      reinterpret_cast<fftw_complex*>(_data_in), reinterpret_cast<fftw_complex*>(_data_in),
      FFTW_BACKWARD, FFTW_PATIENT
    );
    if (!_fftw_backward_plan)
      _LOG(_ERROR_LOG_LEVEL, "(%s: %i) Unable to create backward dft plan.\n", __FILE__, __LINE__);
    
    
    _LOG(_SIMULATION_LOG_LEVEL, " done.\n");
  }
  
  if (_forward) {
    fftw_execute_dft(
      _fftw_forward_plan,
      reinterpret_cast<fftw_complex*>(_data_in),
      reinterpret_cast<fftw_complex*>(_data_in)
    );
  } else {
    fftw_execute_dft(
      _fftw_backward_plan,
      reinterpret_cast<fftw_complex*>(_data_in),
      reinterpret_cast<fftw_complex*>(_data_in)
    );
  }
}


// In-place multiply
void _transform_1(bool _forward, real _multiplier, real* const __restrict__ _data_in, real* const __restrict__ _data_out, ptrdiff_t _prefix_lattice, ptrdiff_t _postfix_lattice)
{
  if (_prefix_lattice <= 0 || _postfix_lattice <= 0) return;
  #pragma ivdep
  for (long _i0 = 0; _i0 < _prefix_lattice * _postfix_lattice; _i0++) {
    _data_in[_i0] *= _multiplier;
  }
}

// ********************************************************
//   Default Simulation Driver function implementations
void _segment0()
{
  tau = 0.0;
  
  _mg0_output_raw_initialise();
  _active_x_wavefunction = _x_wavefunction;
  _x_wavefunction_initialise();
  _mg0_output_index_tau = 0;
  _mg0_sample();
  _segment1();
  
  _mg0_process();
}


// ********************************************************
//   field x function implementations
// initialisation for vector wavefunction
void _x_wavefunction_initialise()
{
  
  long _x_wavefunction_index_pointer = 0;
  #define phi _active_x_wavefunction[_x_wavefunction_index_pointer + 0]
  #define x _x[_index_x + 0]
  #define dx (_dx * (1.0))
  
  for (long _index_x = 0; _index_x < _lattice_x; _index_x++) {
    // The purpose of the following define is to give a (somewhat helpful) compile-time error
    // if the user has attempted to use the propagation dimension variable in the initialisation
    // block of a <vector> element. If they're trying to do this, what they really want is a 
    // <computed_vector> instead.
    #define tau Dont_use_propagation_dimension_tau_in_vector_element_CDATA_block___Use_a_computed_vector_instead
    
    // ********** Initialisation code ***************
    
    phi = exp(-0.5 * x*x/sigma/sigma);
    // **********************************************
    #undef tau
    
    // Increment index pointers for vectors in field x (or having the same dimensions)
    _x_wavefunction_index_pointer += 1 * _x_wavefunction_ncomponents;
    
  }
  #undef x
  #undef dx
  #undef phi
  
  _x_wavefunction_basis = 1;
}


void _x_wavefunction_basis_transform(ptrdiff_t new_basis)
{
  if (_x_wavefunction_basis == new_basis)
    return;
  
  if (_x_wavefunction_basis == -1) {
    _LOG(
      _ERROR_LOG_LEVEL,
      "Error: Attempted to transform the vector 'x_wavefunction' to basis %s, but the vector doesn't have a basis specified yet!\n"
      "       Please report this error to xmds-devel@lists.sourceforge.net\n",
      _basis_identifiers[new_basis]
      );
  }
  
  if (_x_wavefunction_basis_map.count(_basis_pair(_x_wavefunction_basis, new_basis)) == 0) {
    _LOG(
      _ERROR_LOG_LEVEL,
      "Error: We should have information about how to do every needed transform, but it seems we don't for this transform.\n"
      "       The transform is for the vector 'x_wavefunction' from basis %s to basis %s.\n",
      _basis_identifiers[_x_wavefunction_basis], _basis_identifiers[new_basis]
    );
  }
  _basis_transform_t &_t = _x_wavefunction_basis_map[_basis_pair(_x_wavefunction_basis, new_basis)];
  if (_t._transform_steps.size() == 0) {
    _LOG(_ERROR_LOG_LEVEL, "Error: It looks like we tried to create plans for this transform, but failed.\n"
                           "       The transform was for the vector 'x_wavefunction' from basis %s to basis %s.\n",
                           _basis_identifiers[_x_wavefunction_basis], _basis_identifiers[new_basis]);
  }
  real *_source_data = reinterpret_cast<real*>(_active_x_wavefunction);
  real *_dest_data = _auxiliary_array;
  for (vector<_transform_step>::iterator _it = _t._transform_steps.begin(); _it != _t._transform_steps.end(); ++_it) {
    _it->_func(_it->_forward, _t._multiplier, _source_data, _dest_data, _it->_prefix_lattice, _it->_postfix_lattice);
    if (_it->_out_of_place) {
      real *_temp = _source_data;
      _source_data = _dest_data;
      _dest_data = _temp;
    }
  }
  _x_wavefunction_basis = new_basis;
}

// ********************************************************
//   segment 1 (RK4 fixed-step integrator) function implementations
void _segment1()
{
  real _step = 2.0/(real)1000;
  real _noiseStep = 2.0/(real)1000;
  
  
  _x_segment1_x_operators_operator0_result = (complex*) xmds_malloc(sizeof(complex) * MAX(_x_segment1_x_operators_operator0_result_alloc_size,1));
  _active_x_segment1_x_operators_operator0_result = _x_segment1_x_operators_operator0_result;
  _segment1_akfield_x_wavefunction = (complex*) xmds_malloc(sizeof(complex) * MAX(_x_wavefunction_alloc_size,1));
  _segment1_aifield_x_wavefunction = (complex*) xmds_malloc(sizeof(complex) * MAX(_x_wavefunction_alloc_size,1));
  complex* _akfield_x_wavefunction = _segment1_akfield_x_wavefunction;
  complex* _aifield_x_wavefunction = _segment1_aifield_x_wavefunction;
  
  
  for (long _istep = 0; _istep < 1000; _istep++) {
    
    _x_wavefunction_basis_transform(1); // (x)
    
    // a_k = a
    memcpy(_akfield_x_wavefunction, _x_wavefunction, sizeof(complex) * _x_wavefunction_alloc_size);
    
    _segment1_calculate_nonconstant_ip_fields(_step, 1);
    
    // a = D[a]
    _segment1_ip_evolve(1);
    _x_wavefunction_basis_transform(1); // (x)
    
    // a_i = a
    memcpy(_aifield_x_wavefunction, _x_wavefunction, sizeof(complex) * _x_wavefunction_alloc_size);
    
    _active_x_wavefunction = _akfield_x_wavefunction;
      
    // a_k = G[a_k, t]
    _segment1_calculate_delta_a(_step);
    
    // a_k = D[a_k]
    _segment1_ip_evolve(1);
    _x_wavefunction_basis_transform(1); // (x)
    
    for (long _i0 = 0; _i0 < (_lattice_x) * _x_wavefunction_ncomponents; _i0++) {
      // a = a + a_k/6
      _x_wavefunction[_i0] += _akfield_x_wavefunction[_i0]/6.0;
      // a_k = a_i + a_k/2
      _akfield_x_wavefunction[_i0] = _aifield_x_wavefunction[_i0] + 0.5*_akfield_x_wavefunction[_i0];
    }
    
    
    tau += 0.5*_step;
    
    // a_k = G[a_k, t + h/2]
    _segment1_calculate_delta_a(_step);
    _x_wavefunction_basis_transform(1); // (x)
    
    for (long _i0 = 0; _i0 < (_lattice_x) * _x_wavefunction_ncomponents; _i0++) {
      // a = a + a_k/3
      _x_wavefunction[_i0] += _akfield_x_wavefunction[_i0]/3.0;
      // a_k = a_i + a_k/2
      _akfield_x_wavefunction[_i0] = _aifield_x_wavefunction[_i0] + 0.5*_akfield_x_wavefunction[_i0];
    }
    
    
    // a_k = G[a_k, t + h/2]
    _segment1_calculate_delta_a(_step);
    _x_wavefunction_basis_transform(1); // (x)
    
    for (long _i0 = 0; _i0 < (_lattice_x) * _x_wavefunction_ncomponents; _i0++) {
      // a = a + a_k/3
      _x_wavefunction[_i0] += _akfield_x_wavefunction[_i0]/3.0;
      // a_k = a_i + a_k
      _akfield_x_wavefunction[_i0] = _aifield_x_wavefunction[_i0] + _akfield_x_wavefunction[_i0];
    }
    
    
    // a_k = D[a_k]
    _segment1_ip_evolve(1);
    
    tau += 0.5*_step;
    
    // a_k = G[a_k, t + h]
    _segment1_calculate_delta_a(_step);
    _x_wavefunction_basis_transform(1); // (x)
    
    _active_x_wavefunction = _x_wavefunction;
    
    // a = D[a]
    _segment1_ip_evolve(1);
    _x_wavefunction_basis_transform(1); // (x)
    
    for (long _i0 = 0; _i0 < (_lattice_x) * _x_wavefunction_ncomponents; _i0++) {
      // a = a + a_k/6
      _x_wavefunction[_i0] += _akfield_x_wavefunction[_i0]/6.0;
    }
    
    
    if ((_istep % 100) == 99.0)
      _mg0_sample();
  }
  
  _SEGMENT1_END:;
  
  xmds_free(_x_segment1_x_operators_operator0_result);
  _active_x_segment1_x_operators_operator0_result = _x_segment1_x_operators_operator0_result = NULL;
  xmds_free(_segment1_akfield_x_wavefunction);
  xmds_free(_segment1_aifield_x_wavefunction);
  
}


inline void _segment1_calculate_delta_a(real _step)
{
  
  // EX transverse derivative operator for field x (not constant)
  _segment1_x_operators_evaluate_operator0();
  
  // Delta A propagation operator for field x
  _segment1_x_operators_evaluate_operator1(_step);
  
}


inline void _segment1_ip_evolve(int _exponent)
{
}


inline void _segment1_calculate_nonconstant_ip_fields(real _step, int _exponent)
{
}

// EX transverse derivative operator for field x (not constant)
void _segment1_x_operators_evaluate_operator0()
{
  memcpy(_active_x_segment1_x_operators_operator0_result, _active_x_wavefunction, _x_wavefunction_alloc_size * sizeof(complex));
  complex *__backup_ptr = _active_x_wavefunction;
  ptrdiff_t __backup_basis = _x_wavefunction_basis;
  _active_x_wavefunction = _active_x_segment1_x_operators_operator0_result;
  
  // Transforming vectors to basis (kx)
  _x_wavefunction_basis_transform(0); // (kx)
  
  long _x_segment1_x_operators_operator0_result_index_pointer = 0;
  #define _Ltt_phi _active_x_segment1_x_operators_operator0_result[_x_segment1_x_operators_operator0_result_index_pointer + 0]
  long _x_wavefunction_index_pointer = 0;
  #define phi _active_x_wavefunction[_x_wavefunction_index_pointer + 0]
  #define kx _kx[_index_kx + 0]
  #define dkx (_dkx * (1.0))
  
  for (long _index_kx = 0; _index_kx < _lattice_kx; _index_kx++) {
    complex Ltt;
    
    
    // ************** Operator code *****************
    
    Ltt = -i*kx*kx*0.5;
    // **********************************************
    
    // Ltt[phi]
    _Ltt_phi = Ltt * phi;
    // Increment index pointers for vectors in field x (or having the same dimensions)
    _x_wavefunction_index_pointer += 1 * _x_wavefunction_ncomponents;
    _x_segment1_x_operators_operator0_result_index_pointer += 1 * _x_segment1_x_operators_operator0_result_ncomponents;
    
  }
  #undef kx
  #undef dkx
  #undef _Ltt_phi
  #undef phi
  _active_x_wavefunction = __backup_ptr;
  _x_wavefunction_basis = __backup_basis;
  
  _x_segment1_x_operators_operator0_result_basis = 0;
}

void _x_segment1_x_operators_operator0_result_basis_transform(ptrdiff_t new_basis)
{
  if (_x_segment1_x_operators_operator0_result_basis == new_basis)
    return;
  
  if (_x_segment1_x_operators_operator0_result_basis == -1) {
    _LOG(
      _ERROR_LOG_LEVEL,
      "Error: Attempted to transform the vector 'x_segment1_x_operators_operator0_result' to basis %s, but the vector doesn't have a basis specified yet!\n"
      "       Please report this error to xmds-devel@lists.sourceforge.net\n",
      _basis_identifiers[new_basis]
      );
  }
  
  if (_x_segment1_x_operators_operator0_result_basis_map.count(_basis_pair(_x_segment1_x_operators_operator0_result_basis, new_basis)) == 0) {
    _LOG(
      _ERROR_LOG_LEVEL,
      "Error: We should have information about how to do every needed transform, but it seems we don't for this transform.\n"
      "       The transform is for the vector 'x_segment1_x_operators_operator0_result' from basis %s to basis %s.\n",
      _basis_identifiers[_x_segment1_x_operators_operator0_result_basis], _basis_identifiers[new_basis]
    );
  }
  _basis_transform_t &_t = _x_segment1_x_operators_operator0_result_basis_map[_basis_pair(_x_segment1_x_operators_operator0_result_basis, new_basis)];
  if (_t._transform_steps.size() == 0) {
    _LOG(_ERROR_LOG_LEVEL, "Error: It looks like we tried to create plans for this transform, but failed.\n"
                           "       The transform was for the vector 'x_segment1_x_operators_operator0_result' from basis %s to basis %s.\n",
                           _basis_identifiers[_x_segment1_x_operators_operator0_result_basis], _basis_identifiers[new_basis]);
  }
  real *_source_data = reinterpret_cast<real*>(_active_x_segment1_x_operators_operator0_result);
  real *_dest_data = _auxiliary_array;
  for (vector<_transform_step>::iterator _it = _t._transform_steps.begin(); _it != _t._transform_steps.end(); ++_it) {
    _it->_func(_it->_forward, _t._multiplier, _source_data, _dest_data, _it->_prefix_lattice, _it->_postfix_lattice);
    if (_it->_out_of_place) {
      real *_temp = _source_data;
      _source_data = _dest_data;
      _dest_data = _temp;
    }
  }
  _x_segment1_x_operators_operator0_result_basis = new_basis;
}

// Delta A propagation operator for field x
void _segment1_x_operators_evaluate_operator1(real _step)
{
  // Transforming vectors to basis (x)
  _x_segment1_x_operators_operator0_result_basis_transform(1); // (x)
  _x_wavefunction_basis_transform(1); // (x)
  
  long _x_segment1_x_operators_operator0_result_index_pointer = 0;
  #define _Ltt_phi _active_x_segment1_x_operators_operator0_result[_x_segment1_x_operators_operator0_result_index_pointer + 0]
  long _x_wavefunction_index_pointer = 0;
  #define phi _active_x_wavefunction[_x_wavefunction_index_pointer + 0]
  #define x _x[_index_x + 0]
  #define dx (_dx * (1.0))
  
  for (long _index_x = 0; _index_x < _lattice_x; _index_x++) {
    complex dphi_dtau;
    
    #define dtau _step
    
    // ************* Propagation code ***************
    
    dphi_dtau = _Ltt_phi + i*mod2(phi)*phi;
    // **********************************************
    
    #undef dtau
    
    
    _active_x_wavefunction[_x_wavefunction_index_pointer + 0] = dphi_dtau * _step;
    // Increment index pointers for vectors in field x (or having the same dimensions)
    _x_wavefunction_index_pointer += 1 * _x_wavefunction_ncomponents;
    _x_segment1_x_operators_operator0_result_index_pointer += 1 * _x_segment1_x_operators_operator0_result_ncomponents;
    
  }
  #undef x
  #undef dx
  #undef _Ltt_phi
  #undef phi
}

// ********************************************************
//   output function implementations
void _write_output()
{
  _LOG(_SIMULATION_LOG_LEVEL, "Generating output for nlse_minimal\n");
  
  
  char *_xsilFilename = (char*)malloc(256);
  snprintf(_xsilFilename, 256, "%s.xsil", ("nlse_minimal" + gsArgsAndValues).c_str());
  
  FILE* _outfile = _open_xsil_file(_xsilFilename);
  
  if (_outfile) {
    _write_xsil_header(_outfile);
    char _dataFilename[200];
    snprintf(_dataFilename, 200, "%s.h5", ("nlse_minimal" + gsArgsAndValues).c_str());
    
    H5Fclose(H5Fcreate(_dataFilename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT));
  }
  _mg0_write_out(_outfile);
  
  _write_xsil_footer(_outfile);
  _close_xsil_file(_outfile);
  free(_xsilFilename);
  _xsilFilename = NULL;
  _outfile = NULL;
  
}


FILE* _open_xsil_file(const char* _filename)
{
  
  FILE* fp = fopen(_filename, "w");
  
  if (fp == NULL)
    // _LOG will cause the simulation to exit
    _LOG(_ERROR_LOG_LEVEL, "Unable to open output file '%s'.\n"
                           "Exiting.\n", _filename);
  
  return fp;
}

void _close_xsil_file(FILE*& fp)
{
  if (fp)
    fclose(fp);
  fp = NULL;
  
}

void _write_xsil_header(FILE* fp)
{
  if (!fp)
    return;
  fprintf(fp, "<?xml version=\"1.0\" ?><simulation xmds-version=\"2\">\n");
  fprintf(fp, "  <name> nlse_minimal </name>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <author> Mattias Johnsson </author>\n");
  fprintf(fp, "  <description>\n");
  fprintf(fp, "    The nonlinear Schrodinger equation in one dimension, \n");
  fprintf(fp, "    RK4 algorithm. So no adaptive stepping. The idea is to take the\n");
  fprintf(fp, "    generated C code and try to make it use the GPU for more\n");
  fprintf(fp, "    and more parts until it looks like it's working.\n");
  fprintf(fp, "\n");
  fprintf(fp, "    Then we can try to reproduce that C code in the Cheetah templating.\n");
  fprintf(fp, "\n");
  fprintf(fp, "    And then we can move on to more complex scripts.\n");
  fprintf(fp, "  </description>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <features>\n");
  fprintf(fp, "      <benchmark/>\n");
  fprintf(fp, "      <bing/>\n");
  fprintf(fp, "      <fftw plan=\"patient\"/>\n");
  fprintf(fp, "\n");
  fprintf(fp, "      <!-- <auto_vectorise /> -->\n");
  fprintf(fp, "      <globals>\n");
  fprintf(fp, "          <![CDATA[\n");
  fprintf(fp, "          const double sigma = 2.0;\n");
  fprintf(fp, "          ]]>\n");
  fprintf(fp, "       </globals>\n");
  fprintf(fp, "     </features>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <geometry>\n");
  fprintf(fp, "      <propagation_dimension> tau </propagation_dimension>\n");
  fprintf(fp, "      <transverse_dimensions>\n");
  fprintf(fp, "        <dimension domain=\"(-10, 10)\" lattice=\"128\" name=\"x\"/>\n");
  fprintf(fp, "      </transverse_dimensions>\n");
  fprintf(fp, "   </geometry>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <vector dimensions=\"x\" name=\"wavefunction\" type=\"complex\">\n");
  fprintf(fp, "    <components> phi </components>\n");
  fprintf(fp, "    <initialisation>\n");
  fprintf(fp, "      <![CDATA[\n");
  fprintf(fp, "        phi = exp(-0.5 * x*x/sigma/sigma);\n");
  fprintf(fp, "      ]]>\n");
  fprintf(fp, "    </initialisation>\n");
  fprintf(fp, "  </vector>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <sequence>\n");
  fprintf(fp, "    <integrate algorithm=\"RK4\" interval=\"2.0\" steps=\"1000\">\n");
  fprintf(fp, "      <samples> 10 </samples>\n");
  fprintf(fp, "      <operators>\n");
  fprintf(fp, "        <integration_vectors> wavefunction </integration_vectors>\n");
  fprintf(fp, "        <operator kind=\"ex\">\n");
  fprintf(fp, "          <operator_names> Ltt </operator_names>\n");
  fprintf(fp, "          <![CDATA[\n");
  fprintf(fp, "            Ltt = -i*kx*kx*0.5;\n");
  fprintf(fp, "          ]]>\n");
  fprintf(fp, "        </operator>\n");
  fprintf(fp, "        <![CDATA[\n");
  fprintf(fp, "          dphi_dtau = Ltt[phi] + i*mod2(phi)*phi;\n");
  fprintf(fp, "        ]]>\n");
  fprintf(fp, "\n");
  fprintf(fp, "      </operators>\n");
  fprintf(fp, "    </integrate>\n");
  fprintf(fp, "  </sequence>\n");
  fprintf(fp, "\n");
  fprintf(fp, "  <output>\n");
  fprintf(fp, "      <sampling_group basis=\"x\" initial_sample=\"yes\">\n");
  fprintf(fp, "        <moments> density </moments>\n");
  fprintf(fp, "        <dependencies> wavefunction </dependencies>\n");
  fprintf(fp, "        <![CDATA[\n");
  fprintf(fp, "          density = mod2(phi);\n");
  fprintf(fp, "        ]]>\n");
  fprintf(fp, "      </sampling_group>\n");
  fprintf(fp, "  </output>\n");
  
  fprintf(fp, "\n<info>\n");
  fprintf(fp, "Script compiled with XMDS2 version 3.0.0 \"Release the Kraken\" (r3081)\n");
  fprintf(fp, "See http://www.xmds.org for more information.\n");
  fprintf(fp, "</info>\n");
  
}

// In addition to writing the footer (if 'fp' is not NULL)
// this function closes the fp file pointer.
void _write_xsil_footer(FILE* fp)
{
  if (fp) {
    fprintf(fp, "</simulation>\n");
  }
}

// ********************************************************
//   moment group 0 function implementations
void _mg0_sample()
{
  
  // Transforming vectors to basis (tau, x)
  _x_wavefunction_basis_transform(1); // (x)
  
  long _x_wavefunction_index_pointer = 0;
  #define phi _active_x_wavefunction[_x_wavefunction_index_pointer + 0]
  long _mg0_output_raw_index_pointer = 0;
  #define density _active_mg0_output_raw[_mg0_output_raw_index_pointer + 0]
  #define x _x[_index_x + 0]
  #define dx (_dx * (1.0))
  
  for (long _index_x = 0; _index_x < _lattice_x; _index_x++) {
    // Set index pointers explicitly for (some) vectors
    _mg0_output_raw_index_pointer = ( 0
       + _mg0_output_index_tau  * _lattice_x
       + _index_x * 1 ) * _mg0_output_raw_ncomponents;
    #define _SAMPLE_COMPLEX(variable) \
              variable ## R = variable.Re(); variable ## I = variable.Im();
    
    // *************** Sampling code ****************
    
    density = mod2(phi);
    // **********************************************
    
    #undef _SAMPLE_COMPLEX
    // Increment index pointers for vectors in field mg0_sampling (or having the same dimensions)
    _x_wavefunction_index_pointer += 1 * _x_wavefunction_ncomponents;
    
  }
  #undef x
  #undef dx
  #undef phi
  #undef density
  
  _mg0_output_tau[0 + _mg0_output_index_tau++] = tau;
  
  _LOG(_SAMPLE_LOG_LEVEL, "Sampled field (for moment group #1) at tau = %e\n", tau);
  
}


void _mg0_process()
{
  // No post processing needs to be done
}


void _mg0_write_out(FILE* _outfile)
{
  
  if (_outfile) {
    fprintf(_outfile, "\n");
    fprintf(_outfile, "<XSIL Name=\"moment_group_1\">\n");
    fprintf(_outfile, "  <Param Name=\"n_independent\">2</Param>\n");
    fprintf(_outfile, "  <Array Name=\"variables\" Type=\"Text\">\n");
    fprintf(_outfile, "    <Dim>3</Dim>\n");
    fprintf(_outfile, "    <Stream><Metalink Format=\"Text\" Delimiter=\" \\n\"/>\n");
    fprintf(_outfile, "tau x density \n");
    fprintf(_outfile, "    </Stream>\n");
    fprintf(_outfile, "  </Array>\n");
    fprintf(_outfile, "  <Array Name=\"data\" Type=\"double\">\n");
    fprintf(_outfile, "    <Dim>%i</Dim>\n", _mg0_output_lattice_tau);
    fprintf(_outfile, "    <Dim>%i</Dim>\n", _lattice_x);
    fprintf(_outfile, "    <Dim>3</Dim>\n");
  }
  
  
  char _h5Filename[200];
  snprintf(_h5Filename, 200, "%s.h5", ("nlse_minimal" + gsArgsAndValues).c_str());
  
  /* Open the file */
  hid_t hdf5_file = H5Fopen(_h5Filename, H5F_ACC_RDWR, H5P_DEFAULT);
  if (hdf5_file < 0) {
    _LOG(_WARNING_LOG_LEVEL, "Failed to open HDF5 file '%s', will try to create it.", _h5Filename);
    hdf5_file = H5Fcreate(_h5Filename, H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT);
    if (hdf5_file < 0) {
      _LOG(_ERROR_LOG_LEVEL, "Failed to create HDF5 file '%s'. Bailing.", _h5Filename);
    }
  }
  
  /* Create the group for this data */
  hid_t group;
  if (!H5Lexists(hdf5_file, "/1", H5P_DEFAULT))
    group = H5Gcreate(hdf5_file, "/1", H5P_DEFAULT);
  else
    group = H5Gopen(hdf5_file, "/1");
  
  if (_outfile) {
    fprintf(_outfile, "    <Stream><Metalink Format=\"HDF5\" Type=\"Remote\" Group=\"/1\"/>\n");
    fprintf(_outfile, "%s.h5\n", ("nlse_minimal" + gsArgsAndValues).c_str());
    fprintf(_outfile, "    </Stream>\n");
  }
  
  /* Create the coordinate data sets */
  hsize_t coordinate_length;
  hid_t coordinate_dataspace;
  coordinate_length = _mg0_output_lattice_tau;
  coordinate_dataspace = H5Screate_simple(1, &coordinate_length, NULL);
  hid_t dataset_tau;
  if (!H5Lexists(hdf5_file, "/1/tau", H5P_DEFAULT))
    dataset_tau = H5Dcreate(hdf5_file, "/1/tau", H5T_NATIVE_REAL, coordinate_dataspace, H5P_DEFAULT);
  else
    dataset_tau = H5Dopen(hdf5_file, "/1/tau");
  H5Dwrite(dataset_tau, H5T_NATIVE_REAL, H5S_ALL, H5S_ALL, H5P_DEFAULT, _mg0_output_tau);
  #if defined(HAVE_HDF5_HL)
    H5DSset_scale(dataset_tau, "tau");
  #endif
  
  H5Sclose(coordinate_dataspace);
  coordinate_length = _lattice_x;
  coordinate_dataspace = H5Screate_simple(1, &coordinate_length, NULL);
  hid_t dataset_x;
  if (!H5Lexists(hdf5_file, "/1/x", H5P_DEFAULT))
    dataset_x = H5Dcreate(hdf5_file, "/1/x", H5T_NATIVE_REAL, coordinate_dataspace, H5P_DEFAULT);
  else
    dataset_x = H5Dopen(hdf5_file, "/1/x");
  H5Dwrite(dataset_x, H5T_NATIVE_REAL, H5S_ALL, H5S_ALL, H5P_DEFAULT, _x);
  #if defined(HAVE_HDF5_HL)
    H5DSset_scale(dataset_x, "x");
  #endif
  
  H5Sclose(coordinate_dataspace);
  
  hsize_t file_dims[] = {_mg0_output_lattice_tau, _lattice_x};
  hid_t file_dataspace = H5Screate_simple(2, file_dims, NULL);
  
  hid_t dataset_density;
  if (!H5Lexists(hdf5_file, "/1/density", H5P_DEFAULT))
    dataset_density = H5Dcreate(hdf5_file, "/1/density", H5T_NATIVE_REAL, file_dataspace, H5P_DEFAULT);
  else
    dataset_density = H5Dopen(hdf5_file, "/1/density");
  #if defined(HAVE_HDF5_HL)
    H5DSattach_scale(dataset_density, dataset_tau, 0);
    H5DSattach_scale(dataset_density, dataset_x, 1);
  #endif
  H5Dclose(dataset_tau);
  H5Dclose(dataset_x);
  
  
  if ((_mg0_output_lattice_tau * _lattice_x)) {
    /* Create the data space */
    hsize_t file_start[2] = {(unsigned long long int)0, (unsigned long long int)0};
    hsize_t mem_dims[3] = {(unsigned long long int)_mg0_output_lattice_tau, (unsigned long long int)_lattice_x, (unsigned long long int)1};
    hsize_t mem_start[3] = {0, 0, 0};
    hsize_t mem_stride[3] = {1, 1, 1};
    hsize_t mem_count[3] = {(unsigned long long int)_mg0_output_lattice_tau, (unsigned long long int)_lattice_x, (unsigned long long int)1};
    
    
    hid_t mem_dataspace;
    mem_dims[2] = 1;
    mem_dataspace = H5Screate_simple(3, mem_dims, NULL);
    mem_stride[2] = 1;
    
    // Select hyperslabs of memory and file data spaces for data transfer operation
    mem_start[2] = 0;
    H5Sselect_hyperslab(mem_dataspace, H5S_SELECT_SET, mem_start, mem_stride, mem_count, NULL);
    H5Sselect_hyperslab(file_dataspace, H5S_SELECT_SET, file_start, mem_stride, mem_count, NULL);
    
    if (dataset_density)
      H5Dwrite(dataset_density, H5T_NATIVE_REAL, mem_dataspace, file_dataspace, H5P_DEFAULT, _active_mg0_output_raw);
    
    H5Sclose(mem_dataspace);
  }
  
  
  H5Dclose(dataset_density);
  
  H5Sclose(file_dataspace);
  H5Gclose(group);
  H5Fclose(hdf5_file);
  
  
  if (_outfile) {
    fprintf(_outfile, "  </Array>\n");
    fprintf(_outfile, "</XSIL>\n");
  }
}

// ********************************************************
//   field mg0_output function implementations
// initialisation for vector raw
void _mg0_output_raw_initialise()
{
  
  bzero(_active_mg0_output_raw, sizeof(real) * _mg0_output_raw_alloc_size);
}

