Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/Makefile
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/Makefile	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/Makefile	(revision 37876)
@@ -50,4 +50,7 @@
 $(SRC)/relphot_parallel_regions.$(ARCH).o \
 $(SRC)/relphot_parallel_images.$(ARCH).o \
+$(SRC)/relphot_synthphot.$(ARCH).o	 \
+$(SRC)/relphot_synthphot_catalog.$(ARCH).o	 \
+$(SRC)/synthetic_zpts.$(ARCH).o	 \
 $(SRC)/select_images.$(ARCH).o	 \
 $(SRC)/assign_images.$(ARCH).o	 \
@@ -93,4 +96,7 @@
 $(SRC)/relphot_objects.$(ARCH).o	 \
 $(SRC)/relphot_client.$(ARCH).o	 \
+$(SRC)/relphot_synthphot.$(ARCH).o	 \
+$(SRC)/relphot_synthphot_catalog.$(ARCH).o	 \
+$(SRC)/synthetic_zpts.$(ARCH).o	 \
 $(SRC)/client_logger.$(ARCH).o	 \
 $(SRC)/setExclusions.$(ARCH).o 	 \
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/include/relphot.h
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/include/relphot.h	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/include/relphot.h	(revision 37876)
@@ -22,4 +22,5 @@
   PARALLEL_IMAGES,
   APPLY_OFFSETS,
+  SYNTH_PHOT,
 } RelphotMode;
 
@@ -29,5 +30,25 @@
     MODE_UPDATE = 2,
     MODE_UPDATE_OBJECTS = 3,
+    MODE_SYNTH_PHOT = 4,
 } ModeType;
+
+// NOTE: this is only used in special cases where we limit photcode ranges
+typedef enum {
+  PS1_none, 
+  PS1_g = 1, 
+  PS1_r = 2, 
+  PS1_i = 3, 
+  PS1_z = 4, 
+  PS1_y = 5,
+  PS1_w = 6,
+} PS1filters;
+
+typedef struct {
+  Header PHU;
+  Header header[5]; // grizy (matches Nsec in photcodes)
+  Matrix matrix[5];
+  Coords coords;
+  int Nx;
+} SynthZeroPoints;
 
 typedef struct {
@@ -175,4 +196,5 @@
 char   SKY_TABLE[DVO_MAX_PATH];
 int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+char  *SYNTH_ZERO_POINTS;
 
 // globals for parallel region operations
@@ -508,4 +530,13 @@
 int isGPC1stack (int photcode);
 int isGPC1warp  (int photcode);
-
-
+int isGPC1synth (int photcode);
+int whichGPC1filter (int photcode);
+
+
+SynthZeroPoints *SynthZeroPointsLoad (char *filename);
+
+int relphot_synthphot (int hostID, char *hostpath);
+int relphot_synthphot_parallel (SkyList *sky);
+
+int relphot_synthphot_catalog (Catalog *catalog, SynthZeroPoints *zpts);
+int relphot_synthphot_average (Average *average, SecFilt *secfilt, Measure *measure, SynthZeroPoints *zpts);
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/args.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/args.c	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/args.c	(revision 37876)
@@ -366,4 +366,11 @@
     mode = UPDATE_AVERAGES;
   }
+  SYNTH_ZERO_POINTS = NULL;
+  if ((N = get_argument (argc, argv, "-synphot"))) {
+    mode = SYNTH_PHOT;
+    remove_argument (N, &argc, argv);
+    SYNTH_ZERO_POINTS = strcreate (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
   if ((N = get_argument (argc, argv, "-apply-offsets"))) {
     remove_argument (N, &argc, argv);
@@ -465,4 +472,11 @@
     }
     MODE = MODE_UPDATE_OBJECTS;
+    remove_argument (N, &argc, argv);
+  }
+  SYNTH_ZERO_POINTS = NULL;
+  if ((N = get_argument (argc, argv, "-synphot"))) {
+    MODE = MODE_SYNTH_PHOT;
+    remove_argument (N, &argc, argv);
+    SYNTH_ZERO_POINTS = strcreate (argv[N]);
     remove_argument (N, &argc, argv);
   }
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/extra.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/extra.c	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/extra.c	(revision 37876)
@@ -1,3 +1,16 @@
 # include "relphot.h"
+
+// for now (20140710) I need to identify gpc1 chips explicitly.  generalize in the future
+int whichGPC1filter (int photcode) {
+
+  if ((photcode > 10000) && (photcode < 10077)) return PS1_g; // g-band
+  if ((photcode > 10100) && (photcode < 10177)) return PS1_r; // r-band
+  if ((photcode > 10200) && (photcode < 10277)) return PS1_i; // i-band
+  if ((photcode > 10300) && (photcode < 10377)) return PS1_z; // z-band
+  if ((photcode > 10400) && (photcode < 10477)) return PS1_y; // y-band
+  if ((photcode > 10500) && (photcode < 10577)) return PS1_w; // w-band
+
+  return PS1_none;
+}
 
 // for now (20140710) I need to identify gpc1 chips explicitly.  generalize in the future
@@ -39,2 +52,9 @@
   return FALSE;
 }
+
+int isGPC1synth (int photcode) {
+
+  if ((photcode >= 3001) && (photcode <= 3006)) return TRUE; // g-band
+
+  return FALSE;
+}
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot.c	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot.c	(revision 37876)
@@ -17,4 +17,9 @@
       // DOES NOT LOAD THE IMAGE TABLE
       relphot_objects (0, NULL);
+      exit (0);
+
+    case SYNTH_PHOT:
+      // set the mean magnitudes ONLY for SYNPHOT objects
+      relphot_synthphot (0, NULL);
       exit (0);
 
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_client.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_client.c	(revision 37869)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_client.c	(revision 37876)
@@ -87,4 +87,10 @@
     }
 
+    case MODE_SYNTH_PHOT:
+      // set the mean magnitudes ONLY for SYNPHOT objects
+      relphot_synthphot (HOST_ID, HOSTDIR);
+      client_logger_message ("set synth photometry\n");
+      break;
+
     default:
       fprintf (stderr, "impossible!");
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot.c	(revision 37876)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot.c	(revision 37876)
@@ -0,0 +1,144 @@
+# include "relphot.h"
+
+// 1) Look for objects which have only synthetic photometry for any of the grizy filters.
+// 2) apply spatial zero point offsets to synth photometry, then set mean mag based on synth.
+
+int relphot_synthphot_parallel (SkyList *sky);
+
+int relphot_synthphot (int hostID, char *hostpath) {
+
+  off_t i;
+
+  SkyTable *sky = NULL;
+  SkyList *skylist = NULL;
+  Catalog catalog;
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // determine the populated SkyRegions overlapping the requested area (default depth)
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // XXX need to decide how to determine PARALLEL mode...
+  if (PARALLEL && !hostID) {
+    relphot_synthphot_parallel (skylist);
+    return TRUE;
+  }
+
+  // load the ZP corrections here
+  SynthZeroPoints *zpts = SynthZeroPointsLoad (SYNTH_ZERO_POINTS);
+  if (!zpts) exit (2);
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < skylist[0].Nregions; i++) {
+
+    // does this host ID match the desired location for the table?
+    if (!HostTableTestHost(skylist[0].regions[i], hostID)) continue;
+
+    // set up the basic catalog info
+    char hostfile[1024];
+    snprintf (hostfile, 1024, "%s/%s.cpt", hostpath, skylist[0].regions[i]->name);
+    catalog.filename  = hostID ? hostfile : skylist[0].filename[i];
+    catalog.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+    catalog.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+    catalog.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_SECF;
+    catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+    if (!dvo_catalog_open (&catalog, skylist[0].regions[i], VERBOSE, "w")) {
+      fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog.filename);
+      exit (1);
+    }
+    if (!catalog.Naves_disk) {
+      if (VERBOSE) fprintf (stderr, "no data in %s, skipping\n", catalog.filename);
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+
+    relphot_synthphot_catalog (&catalog, zpts);
+
+    if (!UPDATE) {
+      dvo_catalog_unlock (&catalog);
+      dvo_catalog_free (&catalog);
+      continue;
+    }
+    
+    if (VERBOSE) fprintf (stderr, "saving catalog %s\n", catalog.filename);
+    
+    // we can optionally convert output format here
+    // but it would be better to define a dvo crawler program to do this
+    // catalog.catformat = DVO_FORMAT_PS1_V1;  
+    dvo_catalog_save (&catalog, VERBOSE); 
+    dvo_catalog_unlock (&catalog);
+    dvo_catalog_free (&catalog);
+  }
+  
+  return (TRUE);
+}
+
+// CATDIR is supplied globally
+# define DEBUG 1
+int relphot_synthphot_parallel (SkyList *sky) {
+
+  // launch the setphot_client jobs to the parallel hosts
+
+  // load the list of hosts
+  HostTable *table = HostTableLoad (CATDIR, sky->hosts);
+  if (!table) {
+    fprintf (stderr, "ERROR: failure reading Host Table %s for database %s\n", sky->hosts, CATDIR);
+    exit (1);
+  }    
+
+  int i;
+  for (i = 0; i < table->Nhosts; i++) {
+
+    // ensure that the paths are absolute path names
+    char *tmppath = abspath (table->hosts[i].pathname, DVO_MAX_PATH);
+    free (table->hosts[i].pathname);
+    table->hosts[i].pathname = tmppath;
+
+    // options / arguments that can affect relphot_client -synthphot:
+    // VERBOSE, VERBOSE2
+
+    char command[1024];
+    snprintf (command, 1024, "relphot_client -synthphot %s -hostID %d -D CATDIR %s -hostdir %s -region %f %f %f %f", 
+	      SYNTH_ZERO_POINTS, table->hosts[i].hostID, CATDIR, table->hosts[i].pathname, UserPatch.Rmin, UserPatch.Rmax, UserPatch.Dmin, UserPatch.Dmax);
+
+    char tmpline[1024];
+    if (VERBOSE)       { snprintf (tmpline, 1024, "%s -v",              command);                    strcpy (command, tmpline); }
+    if (VERBOSE2)      { snprintf (tmpline, 1024, "%s -vv",             command); 		     strcpy (command, tmpline); }
+    if (UPDATE)        { snprintf (tmpline, 1024, "%s -update",         command); 		     strcpy (command, tmpline); }
+
+    fprintf (stderr, "command: %s\n", command);
+
+    if (PARALLEL_MANUAL) continue;
+
+    if (PARALLEL_SERIAL) {
+      int status = system (command);
+      if (status) {
+	fprintf (stderr, "ERROR running relphot_client\n");
+	exit (2);
+      }
+    } else {
+      // launch the job on the remote machine (no handshake)
+      int errorInfo = 0;
+      int pid = rconnect ("ssh", table->hosts[i].hostname, command, table->hosts[i].stdio, &errorInfo, FALSE);
+      if (!pid) {
+	if (DEBUG) fprintf (stderr, "failure to start %s (error %d)\n", table->hosts[i].hostname, errorInfo);
+	exit (1);
+      }
+      table->hosts[i].pid = pid; // save for future reference
+    }
+  }
+
+  if (PARALLEL_MANUAL) {
+    fprintf (stderr, "run the relphot_client commands above.  when these are done, hit return\n");
+    getchar();
+  }
+  if (!PARALLEL_MANUAL && !PARALLEL_SERIAL) {
+    HostTableWaitJobsGetIO (table, __FILE__, __LINE__, VERBOSE);
+  }
+
+  return TRUE;
+}      
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot_catalog.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot_catalog.c	(revision 37876)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/relphot_synthphot_catalog.c	(revision 37876)
@@ -0,0 +1,98 @@
+# include "relphot.h"
+
+// we've just reloaded the data from disk; we now need to apply the Image/Mosaic/Grid
+// calibrations determined by the rest of the program.  We also need to set the final
+// output dbFlags values
+
+int relphot_synthphot_catalog (Catalog *catalog, SynthZeroPoints *zpts) {
+
+  off_t j;
+
+  int Nsecfilt = catalog[0].Nsecfilt;
+
+  for (j = 0; j < catalog[0].Naverage; j++) {
+    off_t m = catalog[0].average[j].measureOffset;
+    Average *average = &catalog[0].average[j];
+    Measure *measure = &catalog[0].measure[m];
+    SecFilt *secfilt = &catalog[0].secfilt[j*Nsecfilt];
+
+    relphot_synthphot_average (average, secfilt, measure, zpts);
+  }
+  return TRUE;
+}
+
+// set mean of chip measurements (selected by photcode range for now):
+int relphot_synthphot_average (Average *average, SecFilt *secfilt, Measure *measure, SynthZeroPoints *zpts) {
+
+  int i;
+  off_t k;
+
+  // first, determine the state of things for each of the main filters:
+  // in this bit, we are using hardwired photcode values:
+  // PS1_g, PS1_r, PS1_i, PS1_z, PS1_y, PS1_w == (1,2,3,4,5,6); 0 == none
+  // note that Nsec = PS_N - 1
+
+  int havePS1[7];
+  int haveSYN[7];
+  int needSYN[7];
+  int measSYN[7];
+
+  // was: 13.64, 13.76, 13.74, 12.94, 12.01
+  float MaxMagForceSynth[] = {0.0, 13.5, 13.5, 13.5, 13.0, 12.0, 13.5};
+
+  for (i = 0; i < 7; i++) {
+    havePS1[i] = FALSE; // do we have any PS1 measurement for this filter?
+    haveSYN[i] = FALSE; // do we have any synthetic measurement for this filter?
+    needSYN[i] = FALSE; // force the use of the synthetic measurement for this filter?
+    measSYN[i] = -1; // which entries carries the synthetic measurement for this filter?
+  }
+
+  // need to look up the (X,Y) coords from this (R,D) location
+  double X, Y;
+  RD_to_XY (&X, &Y, average->R, average->D, &zpts->coords);
+
+  int Xpix = X;
+  int Ypix = Y;
+  int Npix = Xpix + Ypix*zpts->Nx;
+
+  for (k = 0; k < average[0].Nmeasure; k++) {
+
+    // skip measurements that do not have a valid photcode
+    PhotCode *code = GetPhotcodebyCode (measure[k].photcode);
+    if (!code) continue;
+
+    // is this synth_grizy?
+    // is this PS1?
+
+    // are we a PS1 exposure photcode?
+    if (isGPC1chip(measure[k].photcode)) {
+      int Nfilter = whichGPC1filter (measure[k].photcode);
+      havePS1[Nfilter] = TRUE;
+    }
+    if (isGPC1synth(measure[k].photcode)) {
+      int Nfilter = measure[k].photcode - 3000;
+      haveSYN[Nfilter] = TRUE;
+      measSYN[Nfilter] = k;
+
+      // force the use of SYN even if we have PS1 mags?
+      if (measure[k].M < MaxMagForceSynth[Nfilter]) {
+	needSYN[Nfilter] = TRUE;
+      } 
+    }
+  }
+
+  for (i = 1; i < 6; i++) {
+    if (needSYN[i] || (haveSYN[i] && !havePS1[i])) {
+      int Nsec = i - 1;
+      float *value = (float *) zpts->matrix[Nsec].buffer;
+      float ZP = value[Npix];
+      secfilt[Nsec].M  	 = measure[measSYN[i]].M + ZP;
+      secfilt[Nsec].dM 	 = 0.6;
+      secfilt[Nsec].Mchisq = NAN;
+      secfilt[Nsec].flags |= ID_SECF_USE_SYNTH;
+    }
+  }
+
+  return (TRUE);
+}
+
Index: branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/synthetic_zpts.c
===================================================================
--- branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/synthetic_zpts.c	(revision 37876)
+++ branches/eam_branches/ipp-20150112/Ohana/src/relphot/src/synthetic_zpts.c	(revision 37876)
@@ -0,0 +1,52 @@
+# include "relphot.h"
+
+// the PS1 Synthetic magnitudes (Pitts & Magnier) have known systematic errors due to the
+// POSS plate photometry.  This file contains support code to correct that error based on
+// sky maps of the mean offsets.
+
+static char *extname[5] = {"map_g", "map_r", "map_i", "map_z", "map_y"};
+
+SynthZeroPoints *SynthZeroPointsLoad (char *filename) {
+
+  FILE *f = fopen (filename, "r");
+  if (!f) {
+    fprintf (stderr, "ERROR: cannot open file %s\n", filename);
+    return NULL;
+  }
+
+  // SynthZeroPoint file contains:
+  // PHU header (including WCS)
+  // map_g
+  // map_r
+  // map_i
+  // map_z
+  // map_y
+
+  SynthZeroPoints *zpts = NULL;
+  ALLOCATE (zpts, SynthZeroPoints, 1);
+  
+  if (!gfits_fread_header (f, &zpts->PHU)) {
+    if (VERBOSE) fprintf (stderr, "can't read header\n");
+    fclose (f);
+    return NULL;
+  }
+
+  int i;
+  for (i = 0; i < 5; i++) {
+    if (!gfits_find_Xheader(f, &zpts->header[i], extname[i])) {
+      fprintf (stderr, "ERROR: file %s is missing extension %s\n", filename, extname[i]);
+      exit (2);
+    }
+
+    if (!gfits_fread_matrix (f, &zpts->matrix[i], &zpts->header[i])) {
+      if (VERBOSE) fprintf (stderr, "can't read image for %s\n", extname[i]);
+      exit (2);
+    }
+  }
+
+  GetCoords (&zpts->coords, &zpts->PHU);
+  zpts->Nx = zpts->matrix[0].Naxis[0];
+
+  return zpts;
+}
+
