Note

The following shows the code used to run this marx test. You can inspect it and adapt it to your needs, but you cannot copy and paste it directly because it depends on local $PATH and other environment variables. For example, we use a python function to manage the directory structure for all the images generated by all the tests instead of giving the file name directly to save images.

Chandra Orion Ultradeep project

Python : Make input coordinate table

Coordinates are relative to pointing direction in arcmin

# Note that this code might not run if you directly copy and paste it:
# - Not all import statements are shown here
# - `self` is a reference to a test instance, which allows access to
#   parameters such as the directory where the test is run etc.

'''Make input coordinate table

Coordinates are relative to pointing direction in arcmin'''
import os
from astropy.table import Table
from astropy.io import fits

asolfile = self.get_data_file('asol')
asol = fits.getheader(asolfile, 1)
coup = Table.read(os.path.join(self.pkg_data, 'COUP.tsv'),
                  format='ascii.fast_tab')
tab = Table()
tab['RA'] = (coup['RAJ2000'] - asol['RA_NOM']) * 60
tab['DEC'] = (coup['DEJ2000'] - asol['DEC_NOM']) * 60
tab['weight'] = 10**(coup['Lt'] - 27)
tab['energy'] = coup['<E>']
tab.write('coup.marxin', format='ascii.no_header')

C source code : C code for a grid of sources.

(user.h and jdmath.h are shipped with marx.)

#include <stdio.h>
#include <stdlib.h>
#include <jdmath.h>
#include "user.h"

/* This user source implements many point sources via a file that
 * specifies the source positions and energies.  The current implementation
 * assumes the format:
 *  RA  Dec weight energy
 * Here RA, Dec specifiy the source position, weight specifies the strength
 * of the source in relation to the others.
 */
typedef struct
{
   double cosx, cosy, cosz;
   double weight;
   double energy;
}
Point_Source_Type;

static unsigned int Num_Points;
static Point_Source_Type *Point_Sources;
static unsigned int Max_Num_Points;

static char *do_realloc (char *p, unsigned int len)
{
   if (p == NULL)
     p = malloc (len);
   else
     p = realloc (p, len);

   if (p == NULL)
     fprintf (stderr, "Not enough memory\n");

   return p;
}

static void free_sources (void)
{
   if (Point_Sources == NULL)
     return;

   free ((char *) Point_Sources);
   Point_Sources = NULL;
}

static int add_source (double ra, double dec, double weight, double energy)
{
   Point_Source_Type *p;
   double cosx, cosy, cosz;

   /* Convert to God's units from arc-min */
   ra = ra * (PI/(180.0 * 60.0));
   dec = dec * (PI/(180.0 * 60.0));

   if (Max_Num_Points == Num_Points)
     {
        Max_Num_Points += 32;
        p = (Point_Source_Type *)do_realloc ((char *)Point_Sources, Max_Num_Points * sizeof (Point_Source_Type));
        if (p == NULL)
          {
             free_sources ();
             return -1;
          }
        Point_Sources = p;
     }

   p = Point_Sources + Num_Points;
   /* Note the the minus sign is to generate a vector pointing from the
    * source to the origin
    */
   p->cosx = -cos (dec) * cos (ra);
   p->cosy = -cos (dec) * sin(ra);
   p->cosz = -sin (dec);

   p->weight = weight;
   p->energy = energy;
   Num_Points += 1;

   return 0;
}

static void normalize_sources (void)
{
   double total;
   unsigned int i;

   total = 0;
   for (i = 0; i < Num_Points; i++)
     {
     Point_Sources[i].weight += total;
     total = Point_Sources[i].weight;
     }

   for (i = 0; i < Num_Points; i++)
     Point_Sources[i].weight /= total;

   /* Make sure no round-off error affects the weight of the last point */
   Point_Sources[Num_Points - 1].weight = 1.0;
}

int user_open_source (char **argv, int argc, double area,
                   double cosx, double cosy, double cosz)
{
   FILE *fp;
   char line[1024];
   char *file;
   unsigned int linenum;

   file = argv[0];
   if (file == NULL)
     {
     fprintf (stderr, "UserSource Model requires FILE as argument\n");
     return -1;
     }

   fp = fopen (file, "r");
   if (fp == NULL)
     {
     fprintf (stderr, "Unable to open %s\n", file);
     return -1;
     }

   linenum = 0;
   while (NULL != fgets (line, sizeof (line), fp))
     {
     double ra, dec, weight, energy;

     linenum++;
     if (4 != sscanf (line, "%lf %lf %lf %lf", &ra, &dec, &weight, &energy))
       continue;

     if (weight <= 0.0)
       {
          fprintf (stderr, "weight on line %d of %s must be positive\n",
                   linenum, file);
          free_sources ();
          return -1;
       }

     if (-1 == add_source (ra, dec, weight, energy))
       {
          fclose (fp);
          return -1;
       }
     }

   fclose (fp);
   if (Num_Points == 0)
     {
     fprintf (stderr, "%s contains no sources\n", file);
     return -1;
     }

   normalize_sources ();
   return 0;
}

void user_close_source (void)
{
   free_sources ();
}


int user_create_ray (double *delta_t, double *energy,
                  double *cosx, double *cosy, double *cosz)
{
   double r;
   Point_Source_Type *p;

   p = Point_Sources;

   r = JDMrandom ();
   while (r > p->weight)
     p++;

   *delta_t = -1.0;
   *energy = p->energy;
   *cosx = p->cosx;
   *cosy = p->cosy;
   *cosz = p->cosz;

   return 0;
}

int main (int a, char **b)
{
   (void) a;
   (void) b;
   return 1;
}

Python : compile USER code

marx ships with a few examples of user sources. We pick one of them, copy them to the right directory and compile it with gcc.

# Note that this code might not run if you directly copy and paste it:
# - Not all import statements are shown here
# - `self` is a reference to a test instance, which allows access to
#   parameters such as the directory where the test is run etc.

'''compile USER code

|marx| ships with a few examples of user sources. We pick one
of them, copy them to the right directory and compile it with gcc.
'''
marxpath = self.conf.get('marx', 'path')
src = os.path.join(marxpath,
                   'share', 'doc', 'marx', 'examples', 'user-source')
shutil.copy(os.path.join(src, 'user.h'),
            os.path.join(self.basepath, 'user.h'))

jdmath_h = os.path.join(marxpath, 'include')
jdmath_a = os.path.join(marxpath, 'lib', 'libjdmath.a')

subprocess.call(['gcc',
                 '-shared', 'pnts.c', '-o', 'pnts.so', '-fPIC',
                 '-I' + jdmath_h, jdmath_a])

marx : run marx USER source matching observation

marx RA_Nom=83.821008667 Dec_Nom=-5.39440574511 Roll_Nom=310.908820894 GratingType=NONE ExposureTime=166703.44447 DitherModel=FILE DitherFile=download/primary/pcadf158603295N003_asol1.fits TStart=158603295.252 ACIS_Exposure_Time=3.1 SourceRA=83.819583 SourceDEC=-5.39 DetectorType=ACIS-I DetOffsetX=0.0014398546217 DetOffsetZ=0.0050286306018 OutputDir=COUP SourceType=USER UserSourceFile=pnts.so UserSourceArgs=coup.marxin

marx2fits : turn into fits file

marx2fits --pixadj=EDSER COUP COUP.fits

CIAO : ds9 image of the PSF

In the observation, the brightest sources are piled-up. We don’t bother simulating this here, so we just set the scaling limits to bring out the fainter details and ignore the bright peaks.

ds9 -log -cmap heat download/primary/acisf03744N003_evt2.fits COUP.fits -scale limits 0 2000 -frame 1 -regions command 'text 5:35:15 -5:22:09 # text=Observation font="helvetica 24"' -frame 2 -regions command 'text 5:35:15 -5:22:09 # text=MARX font="helvetica 24"' -region load src.fits -saveimage /melkor/d1/guenther/marx/doc/source/tests/figures/ONC_ds9.png -exit

CIAO : Source detection

dmcopy "COUP.fits[EVENTS][bin x=2500:5500:2,y=2500:5500:2]" im.fits  option=image clobber=yes
celldetect im.fits src.fits clobber=yes

Python : Check position of detected sources

# Note that this code might not run if you directly copy and paste it:
# - Not all import statements are shown here
# - `self` is a reference to a test instance, which allows access to
#   parameters such as the directory where the test is run etc.

'''Check position of detected sources'''
import matplotlib.pyplot as plt
from astropy.table import Table
from astropy.coordinates import SkyCoord
from astropy.io import fits
src = Table.read('src.fits')
srcin = Table.read(os.path.join(self.pkg_data, 'COUP.tsv'),
                   format='ascii.fast_tab')

src_co = SkyCoord(src['RA'], src['DEC'], unit='deg')
srcin_co = SkyCoord(srcin['RAJ2000'], srcin['DEJ2000'], unit='deg')
idx, d2d, d3d = src_co.match_to_catalog_sky(srcin_co)

asolfile = self.get_data_file('asol')
asol = fits.getheader(asolfile, 1)
cen = SkyCoord(asol['RA_NOM'], asol['DEC_NOM'], unit='deg')
det = SkyCoord(src['RA'], src['DEC'], unit='deg')
d = cen.separation(src_co).arcsec

fig = plt.figure()
ax1 = plt.subplot(111)
scat1 = ax1.scatter(d, d2d.arcsec, c=np.10(src['NET_COUNTS']), lw=1)
ax1.set_xlabel('distance from aimpoint [arcsec]')
ax1.set_ylabel('coordinate error [arcsec]')
ax1.set_xlim([0, 350])
ax1.set_ylim([0, 2])
cbar1 = fig.colorbar(scat1, ax=ax1)
cbar1.set_label('log(net counts per source)')

fig.savefig(self.figpath(self.figures.keys()[1]))