Пример #1
0
void MainWindow::displayLSTFile()
{
    QString path = QFileDialog::getOpenFileName(
                this,
                "LST File",
                QDir::currentPath(),
                "LST file (*.lst)");

    if (!path.isNull())
    {

        lastLSTFile = path.toStdString();
        displayWidget->setSourceFile(path.toStdString());
        displayWidget->setCurrent();
        GenerateModel();
        displayWidget->repaint();
        displayWidget->updateGL();
    }
}
Пример #2
0
int main(int argc, char *argv[])
{
   int i;
   char *c,*s,*fn;
   char sBuf[256],fmt[256];

   void       Initialise(void);
   void       ProcessText(char *fn,bool lastFile);
   bool    Exists(char *fn);
   BackOffLM *CombineModels(MemHeap *heap,LMInfo *lmi,int nLModel,int nSize,WordMap *wl) ;

   InitShell(argc,argv,ladapt_version,ladapt_vc_id);
   InitMem();
   InitMath();
   InitWave();
   InitLabel();
   InitLUtil();
   InitWMap();
   InitGBase();
   InitLModel();
   InitPCalc();
   InitPMerge();

   SetConfParms();

   if (!InfoPrinted() && NumArgs() == 0)
      ReportUsage();
   if (NumArgs() == 0) Exit(EXIT_SUCCESS);

   InitBuildInfo(&binfo); 
   binfo.dctype = DC_ABSOLUTE;
   nLModel = 1;
   while (NextArg() == SWITCHARG) {
      s = GetSwtArg();
      if (strlen(s)!=1) 
         HError(16419,"Bad switch %s; must be single letter",s);
      switch(s[0]){
         case 'a':
            newWords = GetChkedInt(10,10000000,s); break;
         case 'b':
            ngbSize = GetChkedInt(10,10000000,s); break;
         case 'c':
            i = GetChkedInt(2,LM_NSIZE,s); 
	    binfo.cutOff[i] = GetChkedInt(0,1000,s);
	    break;
         case 'd':
            if (NextArg()!=STRINGARG)
               HError(16419,"Gram base root file name expected");
            rootFN = GetStrArg(); 
	    break;
         case 'f':
	    strcpy(fmt, GetStrArg());
	    for (c=fmt; *c; *c=toupper(*c), c++); /* To uppercase */
	    if (strcmp(fmt, LM_TXT_TEXT)==0)
	      binfo.saveFmt = LMF_TEXT;
	    else if (strcmp(fmt, LM_TXT_BINARY)==0)
	       binfo.saveFmt = LMF_BINARY;
	    else if (strcmp(fmt, LM_TXT_ULTRA)==0)
	       binfo.saveFmt = LMF_ULTRA;
	    else
	       HError(16419,"Unrecognised LM format, should be one of [%s, %s, %s]",
		      LM_TXT_TEXT, LM_TXT_BINARY, LM_TXT_ULTRA);
	    break;
         case 'g':
            processText = FALSE; break;
	 case 'i':
            if (NextArg()!=FLOATARG)
	       HError(16419,"Interpolation weight expected");
	    lmInfo[nLModel].weight = GetChkedFlt(0.0,1.0,s);
            if (NextArg()!=STRINGARG)
	       HError(16419,"Interpolation LM filename expected");
	    lmInfo[nLModel].fn = GetStrArg();
	    nLModel++;
	    break;
         case 'j':
            i = GetChkedInt(2,LM_NSIZE,s); 
	    binfo.wdThresh[i] = GetChkedFlt(0.0,1E10,s);
	    break;
         case 'n':
            nSize = GetChkedInt(1, MAXNG, s); break;
#ifdef HTK_TRANSCRIBER
         case 's':
            if (NextArg()!=STRINGARG)
               HError(16419,"Gram file text source descriptor expected");
            txtSrc = GetStrArg(); break;
         case 't':
	    binfo.dctype = DC_KATZ; break;
#endif
         case 'w':
            if (NextArg()!=STRINGARG)
               HError(16419,"Word list file name expected");
            wlistFN = GetStrArg(); break;
#ifndef HTK_TRANSCRIBER
         case 'x':
            binfo.ptype = LMP_COUNT; break;
#endif
         case 'T':
            trace = GetChkedInt(0,077,s); break;
         default:
            HError(16419,"LAdapt: Unknown switch %s",s);
      }
   }
#ifdef HTK_TRANSCRIBER
   if (nLModel==1) {  /* must interpolate with at least one model */
      HError(16419,"LAdapt: at least one model must be specified with -i option");
   }
   if (binfo.saveFmt==LMF_TEXT) { /* save fomat cannot be TEXT */ 
      binfo.saveFmt=LMF_BINARY;
   }
#endif
   if (NextArg() != STRINGARG)
      HError(16419,"LAdapt: language model file name expected");
   outFN = CopyString(&gstack,GetStrArg());

   Initialise();
   if (processText) {
      if (NextArg() != STRINGARG)
	 ProcessText(NULL,TRUE);       /* input from stdin */
      else
	 while (NextArg() == STRINGARG) {
	    /* !! copy string argument since it gets overwritten 
	       by NextArg() when reading from script file */
	    fn = CopyString(&gstack,GetStrArg());
	    ProcessText(fn,NextArg() != STRINGARG);
	 }
      if (NumArgs() != 0)
	 HError(-16419,"LAdapt: unused args left on cmd line");
      for (i=0; i<stdBuf.ngb->fndx; i++) {
	 sprintf(sBuf,"%s.%d",stdBuf.ngb->fn,i);  
	 AddInputGFile(&inSet,sBuf,1.0);
      }
      ResetHeap(&langHeap);
   } else {
      for (i=0; i<MAX_NGRAM_FILES; i++) {
	 sprintf(sBuf,"%s.%d",rootFN,i);
	 if (!Exists(sBuf))
	    break;
	 AddInputGFile(&inSet,sBuf,1.0);
      }
      if (i==MAX_NGRAM_FILES)
      {
	HError(-16419, "LAdapt: Only %d n-gram files read (recompile with different setting\nof MAX_NGRAM_FILES");
      }
   }
   if (nLModel==1) {
      adpLM = GenerateModel(&langHeap,&binfo);
   } else {
      if (binfo.ptype==LMP_COUNT) 
	 binfo.ptype = LMP_FLOAT;
      newLM = GenerateModel(&langHeap,&binfo);
      lmInfo[0].lm = newLM;
      lmInfo[0].fn = "unknown";
      /* combine all models into one */
      adpLM = CombineModels(&langHeap,lmInfo,nLModel,nSize,tgtVoc);
   }
#ifdef HTK_TRANSCRIBER
#ifdef HTK_CRYPT
   adpLM->encrypt = TRUE;     /* force to write encrypted model */
#endif
#endif
   SaveLangModel(outFN,adpLM);

   Exit(EXIT_SUCCESS);
   return EXIT_SUCCESS; /* never reached -- make compiler happy */
}
Пример #3
0
void MainWindow::generateNewVariation()
{// This is also temporary
    string basefilename = "treefile";
    string filename;

    stringstream ss;

    if (lastGeneratedFile != "")
    {
        filename = lastGeneratedFile;
    }
    else
    {
        errorMessage("You have not generated a tree this session", "Cannot generate new variation");
        return;
    }

    displayWidget->clearDisplay();

    QProgressDialog p(this);
    //p.setCancelButton(NULL);
    p.setRange(0, 100);
    p.show();
    setCursor(QCursor(Qt::WaitCursor));
    p.setCursor(QCursor(Qt::ArrowCursor));

    connect(&p, SIGNAL(canceled()), this, SLOT(cancelGeneration()));

    p.setValue(0);
    p.setLabelText("Generating tree model");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "./" << filename << " > " << filename << ".lst";
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered when running the tree generation executable.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(50);
    p.setLabelText("Rendering tree model in display");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << filename << ".lst";
    if (!displayWidget->setSourceFile(ss.str()))
    {
        errorMessage("An error was encountered when rendering the generated tree.", "Tree could not be displayed");
        cancelGeneration();
        return;
    }

    GenerateModel();

    p.setValue(100);
    p.setLabelText("Generation complete");
    QCoreApplication::processEvents();

    displayWidget->repaint();
    displayWidget->updateGL();
}
Пример #4
0
void MainWindow::generateFromCurrent()
{
    // This is temporary
    string basefilename = "treefile";
    string filename;
    int count = 0;


    stringstream ss;

    displayWidget->clearDisplay();

    QProgressDialog p(this);
    //p.setCancelButton(NULL);
    p.setRange(0, 100);
    p.setModal(true);
    p.show();
    setCursor(QCursor(Qt::WaitCursor));
    p.setCursor(QCursor(Qt::ArrowCursor));

    connect(&p, SIGNAL(canceled()), this, SLOT(cancelGeneration()));

    p.setValue(0);
    p.setLabelText("Finding new temporary file location");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;

    bool exists;
    do {
        ss.str("");
        ss << basefilename << count;
        filename = ss.str();
        ss << ".xml";
        ifstream file(ss.str().c_str());
        exists = file;
        file.close();
        ++count;
    } while (exists);

    p.setValue(5);
    p.setLabelText("Saving sketch to temporary file");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << filename << ".xml";
    //error checking here?
    sketchWidget->writeToXMLFile(ss.str().c_str());


    p.setValue(10);
    p.setLabelText("Converting sketch to 3D");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "cat " << filename << ".xml | ./SketchConverter/Debug/tree3d > " << filename << ".3d.xml";
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered during the 2D to 3D conversion process.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(30);
    p.setLabelText("Creating L-system definition");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "cat " << filename << ".3d.xml | ./LSystemParameteriser/Debug/lsparm ";
    if (optionD > 0)
        ss << "-d " << optionD << " ";
    if (optionB > 0)
        ss << "-b " << optionB << " ";
    if (optionP > 0)
        ss << "-p " << optionP << " ";
    ss << "--storeroot ";
    if (storeRoot != 0)
        ss << "1 ";
    else
        ss << "0 ";
    ss << "> " << filename << ".lpfg";
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered during the L-system parameterisation process.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(50);
    p.setLabelText("Converting L-system definition to source code");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "cat " << filename << ".lpfg | ./LStringDeriver/Debug/lpfgtocpp > " << filename << ".cpp";
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered during the LPFG conversion process.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(70);
    p.setLabelText("Compiling tree generator");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "cat " << filename << ".cpp | g++ -pipe -xc++ - -O3 -o " << filename;
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered while compiling the tree variation generator.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(90);
    p.setLabelText("Generating tree model");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << "./" << filename << " > " << filename << ".lst";
    if (system(ss.str().c_str()) != 0)
    {
        errorMessage("An error was encountered when running the tree generation executable.", "Could not generate tree");
        cancelGeneration();
        return;
    }

    p.setValue(95);
    p.setLabelText("Rendering tree model in display");
    QCoreApplication::processEvents();
    if (p.wasCanceled()) return;
    ss.str(""); ss << filename << ".lst";
    lastLSTFile = ss.str();
    if (!displayWidget->setSourceFile(ss.str()))
    {
        errorMessage("An error was encountered when rendering the generated tree.", "Tree could not be displayed");
        cancelGeneration();
        return;
    }
    GenerateModel();


    p.setValue(100);
    p.setLabelText("Generation complete");
    QCoreApplication::processEvents();


    displayWidget->setCurrent();

    displayWidget->repaint();
    displayWidget->updateGL();

    lastGeneratedFile = filename;

    setCursor(QCursor(Qt::ArrowCursor));
    newVariation->setEnabled(true);
    // displayCylinderForm->setEnabled(true);
    //   displayCylinderForm->setEnabled(true);
    // generateMesh->setEnabled(true);

}
Пример #5
0
void CHealpixSpheroid::Init()
{
	// See if buffers are allocated, if so free them before re-initing them
	if(mEBO) glDeleteBuffers(1, &mEBO);
	if(mVBO) glDeleteBuffers(1, &mVBO);
	if(mVAO) glDeleteVertexArrays(1, &mVAO);

	const unsigned int n_sides = pow(2, mParams["n_side_power"].getValue());

	n_pixels = nside2npix(n_sides);

	gravity.resize(n_pixels);
	g_x.resize(n_pixels);
	g_y.resize(n_pixels);
	g_z.resize(n_pixels);
	mPixelTemperatures.resize(n_pixels);

	// Generate the verticies and elements
	GenerateModel(mVBOData, mElements);
	// Create a new Vertex Array Object, Vertex Buffer Object, and Element Buffer
	// object to store the model's information.
	//
	// First generate the VAO, this stores all buffer information related to this object
	glGenVertexArrays(1, &mVAO);
	glGenBuffers(1, &mVBO); // Generate 1 buffer
	glGenBuffers(1, &mEBO);

	glBindVertexArray(mVAO);
	// Generate and bind to the VBO. Upload the verticies.
	glBindBuffer(GL_ARRAY_BUFFER, mVBO);
	glBufferData(GL_ARRAY_BUFFER, mVBOData.size() * sizeof(vec3), &mVBOData[0],
			GL_DYNAMIC_DRAW);

	// Generate and bind to the EBO. Upload the elements.
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,
			mElements.size() * sizeof(unsigned int), &mElements[0],
			GL_DYNAMIC_DRAW);

	CHECK_OPENGL_STATUS_ERROR(glGetError(), "Failed to create buffers");

	InitShaderVariables();

	// All done. Un-bind from the VAO, VBO, and EBO to prevent it from being
	// modified by subsequent calls.
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	glActiveTexture(GL_TEXTURE0);
	// Load image as a texture
	glGenTextures(1, &mFluxTextureID);
	glBindTexture(GL_TEXTURE_RECTANGLE, mFluxTextureID);

	glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, 12 * n_sides, n_sides, 0, GL_RGBA,
			GL_FLOAT, &mFluxTexture[0]);

	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	// Set sampler
	GLuint shader_program = mShader->GetProgram();
	GLuint TextureSamp = glGetUniformLocation(shader_program, "TexSampler");
	glUniform1i(TextureSamp, 0); // Set "TexSampler" to user texture Unit 0

	// Return to the default texture.
	glBindTexture(GL_TEXTURE_RECTANGLE, 0);

	// Check that things loaded correctly.
	CHECK_OPENGL_STATUS_ERROR(glGetError(), "Failed bind back to default buffer.");

	// Indicate the model is ready to use.
	mModelReady = true;
}
Пример #6
0
int main(int argc, char * argv[])
{
  // main function to set up the components.  All of this work would be done in Python apart from
  // the GenerateModel function

  // Check we have enough cline-args
  if(argc!=8)
  {
	  printf("This program must be run with file command-line arguments:");
	  printf("eg. ./Inclined_Exponential_Profile <n> <i> <R> <h> <t> <p> <output_name>");
      fflush(stdout);
	  return 1;
  }

  double sersic_n = strtod(argv[1], NULL);
  double inc_angle = strtod(argv[2], NULL);
  double scale_radius = strtod(argv[3], NULL);
  double scale_height = strtod(argv[4], NULL);
  double trunc_factor = strtod(argv[5], NULL);
  double pos_angle = strtod(argv[6], NULL);
  char * output_name = argv[7];

  printf("Sersic Index: %1.1f\n",sersic_n);
  printf("Inclination angle: %1.4f\n",inc_angle);
  printf("Scale radius: %3.2f\n",scale_radius);
  printf("Scale height: %3.2f\n",scale_height);
  printf("Truncation factor: %3.2f\n",trunc_factor);
  printf("Position angle: %1.4f\n",pos_angle);
  fflush(stdout);

  fftw_complex *pixelaverageft, *convmodelft, *PSFft, *dsmodelft, *dsmodel, *xshiftft, *yshiftft;
  double *pixelaverage, *image;
  double rfiducial, sum;
  float **rmodelft, *resampledmodelft, *thickdiskft;
  time_t t1, t2;
  int i, num, x, y, ip;
  int p;
  int hos;
  fftw_plan pixavplan, invplan;

  /* ******************************* */
  // step one:
  // make a circular galaxy surface brighness distribution and r2c FT it
  // this step is done once only at the start of the code, for each Sersic index
  // that we wish to simulate
  int mdim, hmdim, nsersic;
  double *model;
  fftw_complex *modelft;
  fftw_plan bigmodel;
  // set the dimension of the large input circular galaxy.  Note that this must be a large
  // value e.g. 16384 in order to avoid aliasing
  mdim = 16384;
  hmdim = 1 + mdim/2;
  model = (double*)calloc(mdim*mdim, sizeof(double));
  modelft = (fftw_complex*)fftw_malloc( mdim*hmdim*sizeof(fftw_complex) );
  bigmodel = fftw_plan_dft_r2c_2d(mdim, mdim, model, modelft, FFTW_ESTIMATE);
  // make a model with a nominal (fiducial) scalelength
  rfiducial = 30.;
  // make a set of models with different Sersic index values
  nsersic = 1;
  // store the real part of their fourier transform
  rmodelft = (float**)calloc(nsersic, sizeof(float*));
  for (i=0; i<nsersic; i++)
    {
      // allocate memory for this model FT
      rmodelft[i] = (float*)calloc(hmdim, sizeof(float));
      // make a large circular galaxy profile
      makecirculargalaxy(sersic_n, rfiducial, trunc_factor, mdim, model);
      // FFT
      fftw_execute(bigmodel);
      // convert FT complex to float Hankel and store with separate index for each model component
      makehankel(modelft, hmdim, rmodelft[i]);
    }
  // free memory not needed
  free(model);
  free(modelft);
  /* *********************** */

  // set the galaxy parameters - these would be set by the MCMC routine
  double overgalsize = scale_radius*oversampling; // major axis scalelength in pixels in oversampled arrays
  if (overgalsize >= rfiducial/sqrt(2.))
    {
      fflush(stdout);
      fprintf(stderr," error in specifying galaxy size, must be smaller than fiducial size/sqrt(2)\n");
      exit(0);
    }
  double overscaleheight = scale_height*oversampling;
  double xpos = 0.*oversampling;   // x position offset in oversampled pixels
  double ypos = 0.*oversampling;  // y position offset in oversampled pixels

  // allocate oversampled arrays
  // in future, make a selection of sizes to be chosen appropriately
  // to optimise the speed for small galaxy model generation

  int idim, odim, hdim;
  odim = 2*(int)(32.*scale_radius/2.0*pow(sersic_n,2)) ;
  idim = odim*oversampling;  // size of oversampled galaxy image
  hdim = 1 + idim/2;  // x-axis dimension of FFTW hermitian array

  // odim = idim;

  // allocate memory
  // pixel average function and its FT
  pixelaverage = (double*)calloc( idim*idim,sizeof(double) );
  pixelaverageft = (fftw_complex*)fftw_malloc( idim*hdim*sizeof(fftw_complex) );
  // x,y shift FTs
  yshiftft = (fftw_complex*)fftw_malloc( idim*sizeof(fftw_complex) );
  xshiftft = (fftw_complex*)fftw_malloc( hdim*sizeof(fftw_complex) );
  // r2c FFT of convolved model, stored as float
  resampledmodelft = (float*)calloc( idim*hdim,sizeof(float) );
  // r2c FFT of PSF, stored as complex
  PSFft = (fftw_complex*)calloc( idim*hdim,sizeof(fftw_complex) );
  // r2c FFT of thick disk convolving function, stored as complex
  thickdiskft = (float*)calloc( idim*hdim,sizeof(float) );
  // r2c FFT of oversampled, convolved model, stored as complex
  convmodelft = (fftw_complex*)fftw_malloc( idim*hdim*sizeof(fftw_complex) );
  // full FFT of downsampled model
  dsmodelft = (fftw_complex*)fftw_malloc( odim*odim*sizeof(fftw_complex) );
  // complex downsampled image domain model
  dsmodel = (fftw_complex*)calloc( odim*odim,sizeof(fftw_complex) );
  //dsmodel = (double*)calloc( odim*odim,sizeof(double) );
  // real part of downsampeld image domain model
  image = (double*)calloc( odim*odim,sizeof(double) );

  // complex downsampled image domain model
  //fftw_complex* rsmodel = (fftw_complex*)calloc( idim*idim,sizeof(fftw_complex) );
  //double* rsimage = (double*)calloc( idim*idim,sizeof(double) );

  // calculate fftw plans
  // pixelaverage plan
  pixavplan = fftw_plan_dft_r2c_2d(idim, idim, pixelaverage, pixelaverageft, FFTW_ESTIMATE);
  // inverse downsampled image plan
  invplan = fftw_plan_dft_2d(odim, odim, dsmodelft, dsmodel, FFTW_BACKWARD, FFTW_MEASURE);
  //invplan = fftw_plan_dft_c2r_2d(odim, odim, convmodelft, dsmodel, FFTW_ESTIMATE);

  //fftw_plan invplan2 = fftw_plan_dft_2d(odim, odim, convmodelft, rsmodel, FFTW_BACKWARD, FFTW_ESTIMATE);

  // fill up pixelaverage function
  // this function would also only be calculated once at the start of the galaxy measurement
  // set all values to zero
  for (ip=0; ip<idim*idim; ip++)
    {
      pixelaverage[ip]=0.;
    }
  // set a central box to a tophat function which sums to unity
  // set it to be centred in the array so we don't need to swap quadrants at the end
  hos = oversampling/2;
  int cen = idim/2;
  for (y=cen-hos; y<=cen+hos; y++)
    {
      for (x=cen-hos; x<=cen+hos; x++)
        {
          ip = y*idim + x;
          pixelaverage[ip] = 1./(double)(oversampling*oversampling);
        }
    }
  // create FT of pixelaverage
  fftw_execute(pixavplan);


  // create the FT of the PSF.  For now, just set the PSF to be a delta function in image domain
  // i.e. a uniform function in the Fourier domain
  // this function would be filled once for each galaxy component
  for (i=0; i<idim*hdim; i++) PSFft[i] = pixelaverageft[i];

  // choose which sersic index to make
  int sersicindex = 0; // this will make value of sersic index = 1

  double sini = sin(inc_angle);
  double sini_squared = sini*sini;

  double emod;

  if(sini_squared==0)
  {
	  emod = 0.;
  }
  else
  {
	  emod = (2. - sini_squared + 2*sqrt(1-sini_squared))/sini_squared;
  }

  double e1 = emod * cos(2*pos_angle);
  double e2 = emod * sin(2*pos_angle);

  // optional: run a timing test using a loop by setting a high value for num
  num = 1;
  t1 = time(NULL);
  int odimsq = odim*odim;
  for (i=0; i<num; i++)
    {

      /* ********************************************** */
      // this is the call to the C function that
      // generates a downsampled FT of galaxy model
      GenerateModel(e1, e2, overgalsize, overscaleheight, xpos, ypos, rfiducial, odim,
                    idim, mdim, rmodelft[sersicindex], resampledmodelft,
                    PSFft, thickdiskft, xshiftft, yshiftft, convmodelft, dsmodelft);
      /* ********************************************** */

      /* the following sections are all back in the Python function.
         in principle we could do the iFFT step inside the C function but this probably
         does not improve the speed and would mean also passing an fftw_plan into the C function */

      // make downsampled image inverse FFT
      fftw_execute(invplan);

      // take the real part (discard the imaginary part) and scale
      for (p=0; p<odimsq; p++)
        {
          image[p] = creal(dsmodel[p])/(odim*odim);
        }

      // end of timing loop
    }
  t2 = time(NULL);
  if (num>1)
    {
      printf(" time %g \n",difftime(t2,t1)/num);
    }


  sum = 0.;
  // take the real part (discard the imaginary part) and test the normalisation
  for (p=0; p<odimsq; p++)
    {
      sum += image[p];
    }
  printf(" sum %g \n",sum);

  // write out final output image to fits file
  remove(output_name);
  write2Dfits(output_name,image,odim,odim);

  exit(0);
}