Index: /tags/ipp-ops-20220705/psastro/src/psastroAstromGuess.c
===================================================================
--- /tags/ipp-ops-20220705/psastro/src/psastroAstromGuess.c	(revision 42289)
+++ /tags/ipp-ops-20220705/psastro/src/psastroAstromGuess.c	(revision 42290)
@@ -320,13 +320,20 @@
         if (!chip->process || !chip->file_exists || !chip->data_exists) { continue; }
 
-	if (!chip->toFPA || !chip->fromFPA) {
+	// if a chip fails during the calibration, the associated readout->data_exists
+	// gets set to false.  This may be the wrong solution, but it does not break this
+	// analysis here.  Note this is not the chip->data_exists field tested above.
+	// chip->data_exists is only false if the chip data is missing.
+
+	// chip->fromFPA can be NULL if the inversion fails, but I'm not sure
+	// chip->toFPA can be NULL unless it was not in the original model
+	if (!chip->toFPA) {
 	  char *name = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
 	  fprintf (stderr, "no astrom model for %s, skipping\n", name);
 	  continue;
 	}
-
-        // XXX we are currently inconsistent with marking the good vs the bad data
-        // psastroChipAstrom sets data_exists to false if the fit is bad.  this is
-        // probably wrong since it implies there is no data!
+	if (!chip->fromFPA) {
+	  char *name = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+	  fprintf (stderr, "inversion faiulre for %s (%d), will be skipped\n", name, view->chip);
+	}
 
         // skip chips for which the astrometry failed (NASTRO == 0)
@@ -350,4 +357,8 @@
         float astError = psMetadataLookupF32 (&status, updates, "CERROR");
         if (fabs(astError) < 1e-6) goto skip_chip;
+
+	// XXX EAM 2022.09.22 : a more robust analysis would put the corner points
+	// on the chip metadata so we can be certain the old and new corner values
+	// are correctly matched.
 
         psPlane ptCH, ptFP, ptTP;
Index: /tags/ipp-ops-20220705/psastro/src/psastroMosaicOneChip.c
===================================================================
--- /tags/ipp-ops-20220705/psastro/src/psastroMosaicOneChip.c	(revision 42289)
+++ /tags/ipp-ops-20220705/psastro/src/psastroMosaicOneChip.c	(revision 42290)
@@ -148,4 +148,7 @@
     if (order == 1) { minNstar = PS_MAX ( 8, minNstar); }
 
+    // determine fromFPA transformation and apply new transformation to raw & ref stars
+    psastroUpdateChipToFPA (fpa, chip);
+
     bool validSolution = true;
 
@@ -162,4 +165,8 @@
     if (astNstar < minNstar) {
         psLogMsg("psastro", PS_LOG_INFO, "solution uses too few stars: %d < %d", astNstar, minNstar);
+        validSolution = false;
+    }
+    if (!chip->fromFPA) {
+        psLogMsg("psastro", PS_LOG_INFO, "toFPA/fromFPA inversion failure");
         validSolution = false;
     }
@@ -189,8 +196,5 @@
     psMetadataAddF32 (updates, PS_LIST_TAIL, "AST_MRY",   PS_META_REPLACE, "mosaic astrometry Y 10-90 percentile (arcsec)", results->dYrange * plateScale);
 
-    // determine fromFPA transformation and apply new transformation to raw & ref stars
-    psastroUpdateChipToFPA (fpa, chip);
-
-    //plot results
+    // plot results
     pmAstromVisualPlotMosaicOneChip(rawstars, refstars, match, recipe);
 
Index: /tags/ipp-ops-20220705/psastro/src/psastroOneChipFit.c
===================================================================
--- /tags/ipp-ops-20220705/psastro/src/psastroOneChipFit.c	(revision 42289)
+++ /tags/ipp-ops-20220705/psastro/src/psastroOneChipFit.c	(revision 42290)
@@ -217,4 +217,8 @@
         validSolution = false;
     }
+    if (!chip->fromFPA) {
+        psLogMsg("psastro", PS_LOG_INFO, "toFPA/fromFPA inversion failure");
+        validSolution = false;
+    }
 
     // DVO expects NASTRO = 0 if we fail to find a solution
Index: /tags/ipp-ops-20220705/psastro/src/psastroOneChipGrid.c
===================================================================
--- /tags/ipp-ops-20220705/psastro/src/psastroOneChipGrid.c	(revision 42289)
+++ /tags/ipp-ops-20220705/psastro/src/psastroOneChipGrid.c	(revision 42290)
@@ -22,4 +22,6 @@
     bool status;
     pmAstromStats *stats = NULL;
+    char *chipname = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+
 
     // do we need to get a rough initial match?
@@ -49,10 +51,10 @@
     }
 
-    psLogMsg ("psastro", 3, "grid search using %ld raw vs %ld ref stars\n", rawGridStars->n, refGridStars->n);
+    psLogMsg ("psastro", 3, "grid search for chip %s using %ld raw vs %ld ref stars\n",chipname,rawGridStars->n, refGridStars->n);
 
     // find initial offset / rotation / scale
     pmAstromStats *gridStats = pmAstromGridMatch (rawGridStars, refGridStars, recipe);
     if (gridStats == NULL) {
-	psLogMsg ("psastro", 3, "failed to find a grid match solution\n");
+	psLogMsg ("psastro", 3, "failed to find a grid match solution for chip %s\n",chipname);
 	psFree (rawGridStars);
 	psFree (refGridStars);
@@ -60,5 +62,5 @@
 	return false;
     }
-    psLogMsg ("psastro", 3, "basic grid search result - offset: %f,%f pixels, rotation: %f deg\n", gridStats->offset.x, gridStats->offset.y, DEG_RAD*gridStats->angle);
+    psLogMsg ("psastro", 3, "basic grid search result for chip %s - offset: %f,%f pixels, rotation: %f deg\n",chipname, gridStats->offset.x, gridStats->offset.y, DEG_RAD*gridStats->angle);
 
 # if (1) 
@@ -73,5 +75,5 @@
 	return false;
     }
-    psLogMsg ("psastro", 3, "tweak grid search result - offset: %f,%f pixels, rotation: %f deg\n", stats->offset.x, stats->offset.y, DEG_RAD*stats->angle);
+    psLogMsg ("psastro", 3, "tweak grid search result for chip %s - offset: %f,%f pixels, rotation: %f deg\n",chipname, stats->offset.x, stats->offset.y, DEG_RAD*stats->angle);
 # else
     // EAM TEST: skip tweak
@@ -82,5 +84,5 @@
     // adjust the chip.toFPA terms only
     pmAstromGridApply (chip->toFPA, stats);
-    psastroUpdateChipToFPA (fpa, chip); // updates PSASTRO.RAWSTARS and PSASTRO.REFSTARS
+    psastroUpdateChipToFPA (fpa, chip); // updates PSASTRO.RAWSTARS and PSASTRO.REFSTARS (see note below)
     psFree (gridStats);
     psFree (rawGridStars);
@@ -90,2 +92,8 @@
     return true;
 }
+
+/* if psastroUpdateChiptoFPA fails to invert the toFPA transformation, 
+   the ref->chip coordinates will not be set.  This is not a problem
+   at this stage since they are not used in the calculation.  Later
+   passes can still yield a valid solution.
+*/
Index: /tags/ipp-ops-20220705/psastro/src/psastroUtils.c
===================================================================
--- /tags/ipp-ops-20220705/psastro/src/psastroUtils.c	(revision 42289)
+++ /tags/ipp-ops-20220705/psastro/src/psastroUtils.c	(revision 42290)
@@ -109,8 +109,18 @@
 
     psRegion *region = pmChipPixels (chip);
-
     psFree (chip->fromFPA);
     chip->fromFPA = psPlaneTransformInvert (NULL, chip->toFPA, *region, 50, 4);
     psFree (region);
+
+    // XXX EAM 2022.09.22 : for a specific case, psPlaneTransformInvert fails.
+    // This probably means the solution was poor in any case.  
+    // Some options:
+    // 1) skip the chip in psastroAstromGuessCheck (supply bad corners)
+    // 2) mark this chip as bad (return an error here and trap in psastroMosaicOneChip)
+    // 3) warn of the failure:
+    if (!chip->fromFPA) {
+	char *name = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
+	psLogMsg ("psastro", PS_LOG_INFO, "WARNING: failure to invert toFPA for %s", name);
+    }
 
     // loop over cells in this chip
@@ -150,4 +160,5 @@
 		    pmAstromObj *ref = refstars->data[i];
 		    psPlaneTransformApply (ref->chip, chip->fromFPA, ref->FP);
+		    // if chip->fromFPA is NULL (non-invertable), the action is skipped
 		}
 	    }
