gnuastro-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnuastro-commits] master 806e5fd: MakeProfiles builds kernel without a


From: Mohammad Akhlaghi
Subject: [gnuastro-commits] master 806e5fd: MakeProfiles builds kernel without a catalog with --kernel
Date: Wed, 28 Jun 2017 21:49:27 -0400 (EDT)

branch: master
commit 806e5fd9d7c2958953d131e35ccc289319eac147
Author: Mohammad Akhlaghi <address@hidden>
Commit: Mohammad Akhlaghi <address@hidden>

    MakeProfiles builds kernel without a catalog with --kernel
    
    Until now, MakeProfiles needed a catalog as input for profile parameters,
    the reason is that multiple parameters are needed to define a generic
    profile. But a PSF, or convolution kernel, isn't a generic profile and is
    highly constrained: for example its center is fixed (the center of the
    central pixel), the image size is also dependent on the truncation radius,
    and its total sum of values has to be one. So, the only parameters
    necessary are the radial function, its FWHM, and its truncation radius.
    
    Making a kernel (for convolution) is necessary for several Gnuastro
    programs and it is inconvenient to make a catalog every time you need a
    single kernel. So with this commit a `--kernel' option is added to
    MakeProfiles. With this option, the kernel parameters can be defined on the
    command-line, so there is no need to define a catalog. This greatly
    simplifies the creation of kernels/PSFs and testing their effect on the
    analysis steps that need it.
    
    This finishes task #14421.
---
 NEWS                    |   4 +
 bin/mkprof/args.h       |  14 ++
 bin/mkprof/main.h       |   3 +-
 bin/mkprof/mkprof.c     |  25 +++-
 bin/mkprof/oneprofile.c |   2 +-
 bin/mkprof/profiles.c   |   4 +-
 bin/mkprof/ui.c         | 381 ++++++++++++++++++++++++++++++++++++++----------
 bin/mkprof/ui.h         |   3 +-
 doc/gnuastro.texi       | 104 ++++++++-----
 lib/options.c           |  35 ++++-
 tests/during-dev.sh     |   8 +-
 11 files changed, 453 insertions(+), 130 deletions(-)

diff --git a/NEWS b/NEWS
index b249c68..c2ae348 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,10 @@ GNU Astronomy Utilities NEWS                          -*- 
outline -*-
   multiple times and the order of its calling will be used for the column
   containing the center in the respective dimension (in FITS format).
 
+  MakeProfiles: The new `--kernel' option can make a kernel image without
+  the need to define a catalog. With this option, a catalog (or
+  accompanying background image) must not be given.
+
   `gal_fits_img_info' now also returns the name and units of the dataset
   (if they aren't NULL). So it takes two extra arguments.
 
diff --git a/bin/mkprof/args.h b/bin/mkprof/args.h
index 5922ea6..24c8db9 100644
--- a/bin/mkprof/args.h
+++ b/bin/mkprof/args.h
@@ -69,6 +69,20 @@ struct argp_option program_options[] =
       GAL_OPTIONS_NOT_MANDATORY,
       GAL_OPTIONS_NOT_SET
     },
+    {
+      "kernel",
+      UI_KEY_KERNEL,
+      "STR",
+      0,
+      "Parameters to only build one kernel.",
+      GAL_OPTIONS_GROUP_INPUT,
+      &p->kernel,
+      GAL_TYPE_STRING,
+      GAL_OPTIONS_RANGE_ANY,
+      GAL_OPTIONS_NOT_MANDATORY,
+      GAL_OPTIONS_NOT_SET,
+      ui_parse_kernel
+    },
 
 
 
diff --git a/bin/mkprof/main.h b/bin/mkprof/main.h
index 23767dc..5d62b98 100644
--- a/bin/mkprof/main.h
+++ b/bin/mkprof/main.h
@@ -108,6 +108,7 @@ struct mkprofparams
   char             *backhdu;  /* HDU of background image.                 */
   long             naxes[2];  /* Size of the output image.                */
   uint8_t       clearcanvas;  /* Pixels in background image set to zero.  */
+  gal_data_t        *kernel;  /* Parameters to define a kernel.           */
   uint8_t        oversample;  /* Oversampling scale.                      */
   uint8_t          psfinimg;  /* ==1: Build PSF profiles in image.        */
   uint8_t        individual;  /* ==1: Build all catalog separately.       */
@@ -150,7 +151,7 @@ struct mkprofparams
   size_t                num;  /* The number of profiles.                  */
   double                 *x;  /* X axis position of profile center.       */
   double                 *y;  /* Y axis position of profile center.       */
-  int                    *f;  /* Profile function code.                   */
+  uint8_t                *f;  /* Profile function code.                   */
   float                  *r;  /* Radius of profile.                       */
   float                  *n;  /* Index of profile.                        */
   float                  *p;  /* Position angle of profile                */
diff --git a/bin/mkprof/mkprof.c b/bin/mkprof/mkprof.c
index ddcc89a..1da73b7 100644
--- a/bin/mkprof/mkprof.c
+++ b/bin/mkprof/mkprof.c
@@ -126,14 +126,24 @@ saveindividual(struct mkonthread *mkp)
   /* Note that `width' is in FITS format, not C. */
   size_t dsize[2]={mkp->width[1], mkp->width[0]};
 
-  /* Write the name and remove a similarly named file. */
-  asprintf(&filename, "%s%zu_%s", outdir, ibq->id, p->basename);
-  gal_checkset_check_remove_file(filename, 0, p->cp.dontdelete);
+
+  /* Write the name and remove a similarly named file when the `--kernel'
+     option wasn't called. If `--kernel' is called, then we should just use
+     the final merged filename. */
+  if(p->kernel)
+    filename=p->mergedimgname;
+  else
+    {
+      asprintf(&filename, "%s%zu_%s", outdir, ibq->id, p->basename);
+      gal_checkset_check_remove_file(filename, 0, p->cp.dontdelete);
+    }
+
 
   /* Put the array into a data structure */
   data=gal_data_alloc(ibq->img, GAL_TYPE_FLOAT32, 2, dsize, NULL, 0,
                       p->cp.minmapsize, "MockImage", "Brightness", NULL);
 
+
   /* Write the array to file (a separately built PSF doesn't need WCS
      coordinates). */
   if(ibq->ispsf && p->psfinimg==0)
@@ -151,6 +161,7 @@ saveindividual(struct mkonthread *mkp)
     }
   ibq->indivcreated=1;
 
+
   /* Report if in verbose mode. */
   if(!p->cp.quiet)
     {
@@ -158,7 +169,9 @@ saveindividual(struct mkonthread *mkp)
       gal_timing_report(NULL, jobname, 2);
       free(jobname);
     }
-  free(filename);
+
+  /* Clean up. */
+  if(p->kernel==NULL) free(filename);
 }
 
 
@@ -490,10 +503,10 @@ mkprof_write(struct mkprofparams *p)
 
       /* Report if in verbose mode. */
       ++complete;
-      if(!p->cp.quiet)
+      if(!p->cp.quiet && p->num>1)
         {
           asprintf(&jobname, "row %zu complete, %zu left to go",
-                   ibq->id, num-complete);
+                   ibq->id+1, num-complete);
           gal_timing_report(NULL, jobname, 2);
           free(jobname);
         }
diff --git a/bin/mkprof/oneprofile.c b/bin/mkprof/oneprofile.c
index bf275c7..0faeb09 100644
--- a/bin/mkprof/oneprofile.c
+++ b/bin/mkprof/oneprofile.c
@@ -412,7 +412,7 @@ oneprof_set_prof_params(struct mkonthread *mkp)
   mkp->s          = sin( (90-p->p[id]) * DEGREESTORADIANS );
   mkp->q          = p->q[id];
   mkp->brightness = pow( 10, (p->zeropoint - p->m[id]) / 2.5f );
-  mkp->ibq->ispsf = oneprofile_ispsf(p->f[id]);
+  mkp->ibq->ispsf = p->kernel ? 1 : oneprofile_ispsf(p->f[id]);
   mkp->func       = mkp->ibq->func = p->f[id];
 
 
diff --git a/bin/mkprof/profiles.c b/bin/mkprof/profiles.c
index b9e980c..6ee0197 100644
--- a/bin/mkprof/profiles.c
+++ b/bin/mkprof/profiles.c
@@ -41,9 +41,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
 /****************************************************************
  *****************         Profiles:         ********************
  ****************************************************************/
-/* The Gaussian function at a point: junk is only used here to make it
-   conform to the general format of functions in this file: like
-   Sersic(). */
+/* The Gaussian function at a point. */
 double
 Gaussian(struct mkonthread *mkp)
 {
diff --git a/bin/mkprof/ui.c b/bin/mkprof/ui.c
index 98d8e03..29419f7 100644
--- a/bin/mkprof/ui.c
+++ b/bin/mkprof/ui.c
@@ -63,7 +63,7 @@ const char *
 argp_program_bug_address = PACKAGE_BUGREPORT;
 
 static char
-args_doc[] = "[BackgroundImage] Catalog";
+args_doc[] = "[Options] [Catalog]";
 
 const char
 doc[] = GAL_STRINGS_TOP_HELP_INFO PROGRAM_NAME" will create a FITS image "
@@ -109,6 +109,71 @@ enum program_args_groups
 /**************************************************************/
 /*********    Initialize & Parse command-line    **************/
 /**************************************************************/
+static int
+ui_profile_name_read(char *string, size_t row)
+{
+  if( !strcmp("sersic", string) )
+    return PROFILE_SERSIC;
+
+  else if ( !strcmp("moffat", string) )
+    return PROFILE_MOFFAT;
+
+  else if ( !strcmp("gaussian", string) )
+    return PROFILE_GAUSSIAN;
+
+  else if ( !strcmp("point", string) )
+    return PROFILE_POINT;
+
+  else if ( !strcmp("flat", string) )
+    return PROFILE_FLAT;
+
+  else if ( !strcmp("circum", string) )
+    return PROFILE_CIRCUMFERENCE;
+
+  else if ( !strcmp(GAL_BLANK_STRING, string) )
+    error(EXIT_FAILURE, 0, "atleast one profile function is blank");
+
+  else
+    {
+      if(row)
+        error(EXIT_FAILURE, 0, "`%s' not recognized as a profile function "
+              "name in row %zu", string, row);
+      else
+        error(EXIT_FAILURE, 0, "`%s' not recognized as a profile function "
+              "name in values to `--kernel' option", string);
+    }
+
+  return PROFILE_INVALID;
+}
+
+
+
+
+
+static char *
+ui_profile_name_write(int profile_code)
+{
+  switch(profile_code)
+    {
+    case PROFILE_SERSIC:         return "sersic";
+    case PROFILE_MOFFAT:         return "moffat";
+    case PROFILE_GAUSSIAN:       return "gaussian";
+    case PROFILE_POINT:          return "point";
+    case PROFILE_FLAT:           return "flat";
+    case PROFILE_CIRCUMFERENCE:  return "circum";
+    default:
+      error(EXIT_FAILURE, 0, "%s: %d not recognized as a profile code",
+            __func__, profile_code);
+    }
+
+  return NULL;
+}
+
+
+
+
+
+
 static void
 ui_initialize_options(struct mkprofparams *p,
                       struct argp_option *program_options,
@@ -211,6 +276,116 @@ parse_opt(int key, char *arg, struct argp_state *state)
 
 
 
+/* Parse the kernel properties, the format is like this:
+
+     PROFILE_NAME,PARAM_1,PARAM_2,PARAM_3,...,PARAM_N       */
+void *
+ui_parse_kernel(struct argp_option *option, char *arg,
+                char *filename, size_t lineno, void *junk)
+{
+  long profcode;
+  double *darray;
+  gal_data_t *kernel;
+  size_t i, nc, numneeded=0;
+  char *c, *profile, *tailptr;
+  char *str, sstr[GAL_OPTIONS_STATIC_MEM_FOR_VALUES];
+
+  /* We want to print the stored values. */
+  if(lineno==-1)
+    {
+      /* Set the value pointer to kernel. */
+      kernel=*(gal_data_t **)(option->value);
+      darray = kernel->array;
+
+      /* First write the profile function code into the output string. */
+      nc=0;
+      nc += sprintf(sstr+nc, "%s,", ui_profile_name_write(kernel->status));
+
+      /* Write the values into a string. */
+      for(i=0;i<kernel->size;++i)
+        {
+          if( nc > GAL_OPTIONS_STATIC_MEM_FOR_VALUES-100 )
+            error(EXIT_FAILURE, 0, "%s: a bug! please contact us at %s so we "
+                  "can address the problem. The number of necessary "
+                  "characters in the statically allocated string has become "
+                  "too close to %d", __func__, PACKAGE_BUGREPORT,
+                  GAL_OPTIONS_STATIC_MEM_FOR_VALUES);
+          nc += sprintf(sstr+nc, "%g,", darray[i]);
+        }
+      sstr[nc-1]='\0';
+
+      /* Copy the string into a dynamically allocated space, because it
+         will be freed later.*/
+      gal_checkset_allocate_copy(sstr, &str);
+      return str;
+    }
+  else
+    {
+      /* The first part of `arg' (before the first comma) is not
+         necessarily a number. So we need to separate the first part from
+         the rest.*/
+      c=arg;while(*c!='\0' && *c!=',') ++c;
+      profile=arg;
+      arg = (*c=='\0') ? NULL : c+1;  /* `point' doesn't need any numbers. */
+      *c='\0';
+
+      /* Read the parameters. */
+      kernel=gal_options_parse_list_of_numbers(arg, filename, lineno);
+      *(gal_data_t **)(option->value) = kernel;
+
+      /* Write the profile type code into `kernel->status'. If it starts
+         with a digit, then the user might have given the code of the
+         profile directly. In that case, parse the number. Otherwise,
+         let `ui_profile_name_read' find the value. */
+      if( isdigit(*profile) )
+        {
+          profcode=strtol(profile, &tailptr, 0);
+          if(*tailptr!='\0')
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' "
+                          "couldn't be read as a profile code", profile);
+          if(profcode<=0 || profcode>=PROFILE_MAXIMUM_CODE)
+            error_at_line(EXIT_FAILURE, 0, filename, lineno, "`%s' "
+                          "isn't a valid profile code. Please run with "
+                          "`--help' and see the acceptable codes in "
+                          "explanation of the `--fcol' option", profile);
+          kernel->status=profcode;
+        }
+      else
+        kernel->status=ui_profile_name_read(profile, 0);
+
+      /* Make sure the number of parameters conforms with the profile. */
+      switch(kernel->status)
+        {
+        case PROFILE_SERSIC:        numneeded=3;     break;
+        case PROFILE_MOFFAT:        numneeded=3;     break;
+        case PROFILE_GAUSSIAN:      numneeded=2;     break;
+        case PROFILE_POINT:         numneeded=0;     break;
+        case PROFILE_FLAT:          numneeded=1;     break;
+        case PROFILE_CIRCUMFERENCE: numneeded=1;     break;
+        default:
+          error_at_line(EXIT_FAILURE, 0, filename, lineno, "%s: a bug! "
+                        "Please contact us at %s to correct the issue. "
+                        "Profile code %d is not recognized", __func__,
+                        PACKAGE_BUGREPORT, kernel->status);
+        }
+
+      /* Make sure the number of parameters given are the same number that
+         are needed. */
+      if( kernel->size != numneeded )
+        error_at_line(EXIT_FAILURE, 0, filename, lineno, "as a kernel, a "
+                      "`%s' profile needs %zu parameters, but %zu is given",
+                      ui_profile_name_write(kernel->status), numneeded,
+                      kernel->size);
+
+      /* Our job is done, return NULL. */
+      return NULL;
+    }
+}
+
+
+
+
+
 
 
 
@@ -245,16 +420,20 @@ ui_read_check_only_options(struct mkprofparams *p)
      neighter coordinates are specified there is no problem, the user might
      have input the other coordinate standard. We'll also check for that
      after this.*/
-  if( ((p->xcol==NULL) + (p->ycol==NULL)) == 1 )
-    error(EXIT_FAILURE, 0, "only `%s' has been given, please also specify "
-          "a column for the position along the %s axis with the `%s' option",
-          p->xcol?"xcol":"ycol", p->xcol?"Y":"X", p->xcol?"ycol":"xcol");
-
-  if( ((p->racol==NULL) + (p->deccol==NULL)) == 1 )
-    error(EXIT_FAILURE, 0, "only `%s' has been given, please also specify "
-          "a column for the position along the %s axis with the `%s' option",
-          p->racol?"racol":"deccol", p->racol?"Dec":"RA",
-          p->xcol?"deccol":"racol");
+  if(p->kernel==NULL)
+    {
+      if( ((p->xcol==NULL) + (p->ycol==NULL)) == 1 )
+        error(EXIT_FAILURE, 0, "only `%s' has been given, please also "
+              "specify a column for the position along the %s axis with "
+              "the `%s' option", p->xcol?"xcol":"ycol", p->xcol?"Y":"X",
+              p->xcol?"ycol":"xcol");
+
+      if( ((p->racol==NULL) + (p->deccol==NULL)) == 1 )
+        error(EXIT_FAILURE, 0, "only `%s' has been given, please also "
+              "specify a column for the position along the %s axis with "
+              "the `%s' option", p->racol?"racol":"deccol",
+              p->racol?"Dec":"RA", p->xcol?"deccol":"racol");
+    }
 }
 
 
@@ -269,19 +448,31 @@ ui_check_options_and_arguments(struct mkprofparams *p)
   int d0f1;
   char *tmpname;
 
-  /* Make sure an input catalog is given, and if it is FITS, that the HDU
-     is also provided. */
-  if(p->catname)
+  /* If no kernel is given, make sure an input catalog is given, and if it
+     is FITS, that the HDU is also provided. When a kernel option, we will
+     set a fiducial catalog name called `kernel.txt' to automatic output
+     filename generation. */
+  if(p->kernel)
     {
-      if( gal_fits_name_is_fits(p->catname) && p->cp.hdu==NULL)
-        error(EXIT_FAILURE, 0, "no `hdu' specified for the input FITS table "
-              "'%s', to ", p->catname);
+      if(p->catname)
+        error(EXIT_FAILURE, 0, "`--kernel' cannot be called with an input "
+              "catalog (`%s'). The parameters necessary to build a single "
+              "kernel output should be given to `--kernel', not in a "
+              "catalog", p->catname);
+      p->catname="kernel.option";
     }
   else
     {
-      error(EXIT_FAILURE, 0, "no input catalog provided. To build profiles, "
-            "you need to give a catalog/table containing the information of "
-            "the profiles");
+      if(p->catname)
+        {
+          if( gal_fits_name_is_fits(p->catname) && p->cp.hdu==NULL)
+            error(EXIT_FAILURE, 0, "no `hdu' specified for the input FITS "
+                  "table '%s', to ", p->catname);
+        }
+      else
+        error(EXIT_FAILURE, 0, "no input catalog provided. To build "
+              "profiles, you need to give a catalog/table containing "
+              "the information of the profiles");
     }
 
 
@@ -309,8 +500,10 @@ ui_check_options_and_arguments(struct mkprofparams *p)
     }
   p->basename=gal_checkset_not_dir_part(p->mergedimgname);
 
-  /* If a merged image is requested, then delete it if it exists. */
-  if(p->nomerged==0)
+
+  /* If a merged image is requested (or `--kernel' the option is called),
+     then delete the final filename if it exists. */
+  if(p->nomerged==0 && p->kernel)
     gal_checkset_check_remove_file(p->mergedimgname, p->cp.keep,
                                    p->cp.dontdelete);
 }
@@ -338,50 +531,11 @@ ui_check_options_and_arguments(struct mkprofparams *p)
 /***************       Preparations         *******************/
 /**************************************************************/
 static void
-ui_read_profile_function(struct mkprofparams *p, char **strarr)
-{
-  size_t i;
-
-  p->f=gal_data_malloc_array(GAL_TYPE_INT32, p->num, __func__, "p->f");
-  for(i=0;i<p->num;++i)
-    {
-      if( !strcmp("sersic", strarr[i]) )
-        p->f[i]=PROFILE_SERSIC;
-
-      else if ( !strcmp("moffat", strarr[i]) )
-        p->f[i]=PROFILE_MOFFAT;
-
-      else if ( !strcmp("gaussian", strarr[i]) )
-        p->f[i]=PROFILE_GAUSSIAN;
-
-      else if ( !strcmp("point", strarr[i]) )
-        p->f[i]=PROFILE_POINT;
-
-      else if ( !strcmp("flat", strarr[i]) )
-        p->f[i]=PROFILE_FLAT;
-
-      else if ( !strcmp("circum", strarr[i]) )
-        p->f[i]=PROFILE_CIRCUMFERENCE;
-
-      else if ( !strcmp(GAL_BLANK_STRING, strarr[i]) )
-        error(EXIT_FAILURE, 0, "profile function column has blank values. "
-              "Input columns cannot contain blank values");
-      else
-        error(EXIT_FAILURE, 0, "`%s' not recognized as a profile function "
-              "name in row %zu", strarr[i], i);
-    }
-}
-
-
-
-
-
-static void
 ui_read_cols(struct mkprofparams *p)
 {
   int checkblank;
-  char *colname=NULL;
   size_t counter=0, i;
+  char *colname=NULL, **strarr;
   gal_list_str_t *colstrs=NULL;
   gal_data_t *cols, *tmp, *corrtype=NULL;
   char *ax1col=p->racol?p->racol:p->xcol;
@@ -436,7 +590,11 @@ ui_read_cols(struct mkprofparams *p)
         case 7:
           if(tmp->type==GAL_TYPE_STRING)
             {
-              ui_read_profile_function(p, tmp->array);
+              p->f=gal_data_malloc_array(GAL_TYPE_INT32, p->num,
+                                         __func__, "p->f");
+              strarr=tmp->array;
+              for(i=0;i<p->num;++i)
+                p->f[i]=ui_profile_name_read(strarr[i], i+1);
               gal_data_free(tmp);
               corrtype=NULL;
             }
@@ -503,10 +661,10 @@ ui_read_cols(struct mkprofparams *p)
         /* If the index isn't recognized, then it is larger, showing that
            there was more than one match for the given criteria */
         default:
-          gal_tableintern_error_col_selection(p->catname, p->cp.hdu, "too many 
"
-                                              "columns were selected by the "
-                                              "given values to the options "
-                                              "ending in `col'.");
+          gal_tableintern_error_col_selection(p->catname, p->cp.hdu, "too "
+                                              "many columns were selected "
+                                              "by the given values to the "
+                                              "options ending in `col'.");
         }
 
       /* Sanity check and clean up.  Note that it might happen that the
@@ -534,6 +692,61 @@ ui_read_cols(struct mkprofparams *p)
 
 
 
+/* It is possible to define the internal catalog through a catalog or the
+   `--kernel' option. This function will do the job. */
+static void
+ui_prepare_columns(struct mkprofparams *p)
+{
+  float r, n, t;
+  double *karr;
+
+  /* If the kernel option was called, then we need to build a series of
+     single element columns to create an internal catalog. */
+  if(p->kernel)
+    {
+      /* Number of profiles to be built. */
+      p->num=1;
+
+      /* Allocate the necessary columns. */
+      p->x=gal_data_malloc_array(GAL_TYPE_FLOAT64, 1, __func__, "p->x");
+      p->y=gal_data_malloc_array(GAL_TYPE_FLOAT64, 1, __func__, "p->y");
+      p->f=gal_data_malloc_array(GAL_TYPE_UINT8,   1, __func__, "p->f");
+      p->r=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->r");
+      p->n=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->n");
+      p->p=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->p");
+      p->q=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->q");
+      p->m=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->m");
+      p->t=gal_data_malloc_array(GAL_TYPE_FLOAT32, 1, __func__, "p->t");
+
+      /* Set the values that need special consideration. */
+      if(p->kernel->size)
+        {
+          karr=p->kernel->array;
+          r = karr[0];
+          n = p->kernel->size==2 ? 0.0f : karr[1];
+          t = p->kernel->size==1 ? 1.0f : karr[ p->kernel->size - 1 ];
+        }
+      else r=n=t=0.0f;
+
+      /* Fill the allocated spaces. */
+      p->x[0] = 0.0f;
+      p->y[0] = 0.0f;
+      p->f[0] = p->kernel->status;
+      p->r[0] = r;
+      p->n[0] = n;
+      p->p[0] = 0.0f;
+      p->q[0] = 1.0f;
+      p->m[0] = 0.0f;
+      p->t[0] = t;
+    }
+  else
+    ui_read_cols(p);
+}
+
+
+
+
+
 static void
 ui_prepare_wcs(struct mkprofparams *p)
 {
@@ -600,6 +813,11 @@ ui_prepare_canvas(struct mkprofparams *p)
      image to build the profiles over. */
   if(p->backname)
     {
+      /* Make sure the kernel option is not called. */
+      if(p->kernel)
+        error(EXIT_FAILURE, 0, "the `--kernel' and `--background' options "
+              "cannot be called together");
+
       /* Small sanity check. */
       if(p->backhdu==NULL)
         error(EXIT_FAILURE, 0, "no hdu specified for the background image "
@@ -636,7 +854,6 @@ ui_prepare_canvas(struct mkprofparams *p)
     }
   else
     {
-
       /* If any of xshift or yshift is non-zero, the other should be too!
          Note that conditional operators return 1 if true and 0 if false,
          so if one is non-zero while the other is zero, then sum will be
@@ -800,6 +1017,7 @@ ui_finalize_coordinates(struct mkprofparams *p)
 
 
 
+
 /* Add all the columns of the log file. Just note that since this is a
    linked list, we have to add them in the opposite order. */
 static void
@@ -849,9 +1067,17 @@ ui_make_log(struct mkprofparams *p)
 static void
 ui_preparations(struct mkprofparams *p)
 {
+  /* If the kernel option was given, some parameters need to be
+     over-written: */
+  if(p->kernel)
+    {
+      p->nomerged=1;
+      p->psfinimg=0;
+      p->individual=1;
+    }
 
   /* Read in all the columns. */
-  ui_read_cols(p);
+  ui_prepare_columns(p);
 
   /* Prepare the output canvas. */
   ui_prepare_canvas(p);
@@ -898,8 +1124,12 @@ ui_print_intro(struct mkprofparams *p)
 
   printf(PROGRAM_NAME" started on %s", ctime(&p->rawtime));
 
-  asprintf(&jobname, "%zu profile%sread from %s", p->num,
-           p->num>1?"s ":" ", p->catname);
+  if(p->kernel)
+    asprintf(&jobname, "Building one %s kernel",
+             ui_profile_name_write(p->kernel->status));
+  else
+    asprintf(&jobname, "%zu profile%sread from %s", p->num,
+             p->num>1?"s ":" ", p->catname);
   gal_timing_report(NULL, jobname, 1);
   free(jobname);
 
@@ -926,9 +1156,12 @@ ui_print_intro(struct mkprofparams *p)
       free(jobname);
     }
 
-  asprintf(&jobname, "Using %zu threads.", p->cp.numthreads);
-  gal_timing_report(NULL, jobname, 1);
-  free(jobname);
+  if(p->kernel==NULL)
+    {
+      asprintf(&jobname, "Using %zu threads.", p->cp.numthreads);
+      gal_timing_report(NULL, jobname, 1);
+      free(jobname);
+    }
 }
 
 
diff --git a/bin/mkprof/ui.h b/bin/mkprof/ui.h
index 3b948b2..c7361cb 100644
--- a/bin/mkprof/ui.h
+++ b/bin/mkprof/ui.h
@@ -32,7 +32,7 @@ along with Gnuastro. If not, see 
<http://www.gnu.org/licenses/>.
    Available letters (-V which is used by GNU is also removed):
 
    a b d g j l n u v
-   A E G H J L O Q W
+   A G H J L O Q W
 */
 enum option_keys_enum
 {
@@ -42,6 +42,7 @@ enum option_keys_enum
   UI_KEY_NAXIS1          = 'x',
   UI_KEY_NAXIS2          = 'y',
   UI_KEY_CLEARCANVAS     = 'C',
+  UI_KEY_KERNEL          = 'E',
   UI_KEY_OVERSAMPLE      = 's',
   UI_KEY_INDIVIDUAL      = 'i',
   UI_KEY_NOMERGED        = 'm',
diff --git a/doc/gnuastro.texi b/doc/gnuastro.texi
index cccb440..bf942c6 100644
--- a/doc/gnuastro.texi
+++ b/doc/gnuastro.texi
@@ -14188,15 +14188,14 @@ familiar with these concepts.
 @subsubsection Defining an ellipse
 
 @cindex Ellipse
-The PSF, see @ref{PSF}, and galaxy radial profiles are generally
-defined on an ellipse so in this section first defining an ellipse on
-a pixelated 2D surface is discussed. Labeling the major axis of an
-ellipse @mymath{a}, and its minor axis with @mymath{b}, the axis ratio
-is defined as: @mymath{q\equiv b/a}. The major axis of an ellipse can
-be aligned in any direction, therefore define the angle of the major
-axis to the horizontal axis of the image is defined to be the position
-angle of the ellipse and in this book, we show it with
address@hidden
+The PSF, see @ref{PSF}, and galaxy radial profiles are generally defined on
+an ellipse so in this section first defining an ellipse on a pixelated 2D
+surface is discussed. Labeling the major axis of an ellipse @mymath{a}, and
+its minor axis with @mymath{b}, the axis ratio is defined as:
address@hidden b/a}. The major axis of an ellipse can be aligned in any
+direction, therefore the angle of the major axis with respect to the
+horizontal axis of the image is defined to be the position angle of the
+ellipse and in this book, we show it with @mymath{\theta}.
 
 @cindex Radial profile on ellipse
 Our aim is to put a radial profile of any functional form
@@ -14694,7 +14693,7 @@ either individually or in one image. The executable 
name is
 @file{astmkprof} with the following general template
 
 @example
-$ astmkprof [OPTION ...] [BackgroundImage] Catalog
+$ astmkprof [OPTION ...] [Catalog]
 @end example
 
 @noindent
@@ -14707,6 +14706,9 @@ $ astmkprof catalog.txt
 ## Make the profiles in catalog.txt over image.fits:
 $ astmkprof --background=image.fits catalog.txt
 
+## Make a Moffat PSF with FWHM 3pix, beta=2.8, truncation=5
+$ astmkprof --kernel=moffat,2.8,5 --oversample=1
+
 ## Make profiles in catalog, using RA and Dec in the given column:
 $ astmkprof --racol=RA_CENTER --ycol=DEC_CENTER catalog.txt
 
@@ -14716,28 +14718,29 @@ $ astmkprof --individual --oversample 3 -x500 -y500 
catalog.txt
 @end example
 
 @noindent
-If mock images are to be made, a catalog (which stores the parameters for
-each mock profile) is mandatory. The catalog can be in the FITS ASCII, FITS
-binary format, or plain text formats (see @ref{Tables}). The
-columns related to each parameter can be determined both by number, or by
-match/search criteria using the column names, units, or comments. with the
-options ending in @option{col}, see below. Without any file given to the
address@hidden option, MakeProfiles will make the fully zero-valued
-image and build the profiles on that (its size can be set with the
address@hidden and @option{--naxis2} options, and its main WCS parameters
-can also be defined). Besides the main image containing all the profiles it
-is also possible to build on individual images (only enclosing one full
-profile to its truncation radius) with the @option{--individual}.
-
-If a data image file (see @ref{Arguments}) is given, the pixels of
-that image are used as the background value for every pixel. The flux
-value of each profile pixel will be added to the pixel in that
-background value. In this case the values to all options relating to
-the output size and WCS will be ignored if specified (for example
address@hidden, @option{--naxis2} and @option{--prepforconv}) on
-the command-line or in the configuration files. Note that
address@hidden will remain active even if a background image is
-specified.
+The parameters of the mock profiles can either be given through a catalog
+(which stores the parameters of many mock profiles), or the
address@hidden option (see @ref{MakeProfiles options}). The catalog can
+be in the FITS ASCII, FITS binary format, or plain text formats (see
address@hidden). The columns related to each parameter can be determined both
+by number, or by match/search criteria using the column names, units, or
+comments. with the options ending in @option{col}, see below.
+
+Without any file given to the @option{--background} option, MakeProfiles
+will make a zero-valued image and build the profiles on that (its size and
+main WCS parameters can also be defined through the options). Besides the
+main/merged image containing all the profiles in the catalog, it is also
+possible to build individual images for each profile (only enclosing one
+full profile to its truncation radius) with the @option{--individual}
+option.
+
+If an image is given to the @option{--background} option, the pixels of
+that image are used as the background value for every pixel. The flux value
+of each profile pixel will be added to the pixel in that background
+value. In this case, the values to all options relating to the output size
+and WCS will be ignored if specified (for example @option{--oversample},
address@hidden, @option{--naxis2} and @option{--prepforconv}) on the
+command-line or in the configuration files.
 
 Please see @ref{Sufi simulates a detection} for a very complete tutorial
 explaining how one could use MakeProfiles in conjunction with other
@@ -14803,6 +14806,41 @@ Output:
 
 @table @option
 
address@hidden -E STR/INT,FLT[,FLT,[...]]
address@hidden --kernel=STR/INT,FLT[,FLT,[...]]
+Only build one kernel profile with the parameters given as the values to
+this option. The different values must be separated by a comma
+(@key{,}). The first value identifies the radial function of the profile,
+either through a string or through a number (see description of
address@hidden below). Each radial profile needs a different total number
+of parameters: S@'ersic and Moffat functions need 3 parameters: radial,
+S@'ersic index or Moffat @mymath{\beta}, and truncation radius. The
+Gaussian function needs two parameters: radial and truncation radius. The
+point function doesn't need any parameters and flat and circumference
+profiles just need one parameter (truncation radius).
+
+The PSF or kernel is a unique (and highly constrained) type of profile: the
+sum of its pixels must be one, its center must be the center of the central
+pixel (in an image with an odd number of pixels on each side), and commonly
+it is circular, so its axis ratio and position angle are one and zero
+respectively. Kernels are commonly necessary for various data analysis and
+data manipulation steps (for example see @ref{Convolve}, and
address@hidden Because of this it is inconvenient to define a catalog
+with one row and many zero valued columns (for all the non-necessary
+parameters). Hence, with this option, it is possible to create a kernel
+with MakeProfiles without the need to create a catalog. Here are some
+examples:
+
address@hidden @option
address@hidden --kernel=moffat,3,2.8,5
+A Moffat kernel with FWHM of 3 pixels, @mymath{\beta=2.8} which is
+truncated at 5 times the FWHM.
+
address@hidden --kernel=gaussian,2,3
+A Gaussian kernel with FWHM of 2 pixels and truncated at 3 times the FWHM.
address@hidden table
+
+
 @item -k STR
 @itemx --background=STR
 A background image FITS file to build the profiles on. The extension that
@@ -17840,7 +17878,7 @@ acceptable values to this element are defined in 
@ref{Table input
 output}. Based on C's @code{printf} standards.
 
 @item disp_precision
-Width of printing each element of the dataset to a plain text file, the
+Precision of printing each element of the dataset to a plain text file, the
 acceptable values to this element are defined in @ref{Table input
 output}. Based on C's @code{printf} standards.
 
diff --git a/lib/options.c b/lib/options.c
index 3c25003..e832487 100644
--- a/lib/options.c
+++ b/lib/options.c
@@ -175,9 +175,8 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
      (largest size_t number), so the values are kept in memory. */
   size_t minmapsize=-1;
 
-
   /* Go through the input character by character. */
-  while(*c!='\0')
+  while(string && *c!='\0')
     {
       switch(*c)
         {
@@ -210,6 +209,13 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
           ++c;
           break;
 
+        /* Extra dot is an error (cases like 2.5.5). Valid `.'s will be
+           read by `strtod'. */
+        case '.':
+          error_at_line(EXIT_FAILURE, 0, filename, lineno, "extra `.' in "
+                        "`%s'", string);
+          break;
+
         /* Read the number. */
         default:
 
@@ -247,11 +253,26 @@ gal_options_parse_list_of_numbers(char *string, char 
*filename, size_t lineno)
 
 
   /* Allocate the output data structure and fill it up. */
-  i=num;
-  out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &num, NULL, 0,
-                     minmapsize, NULL, NULL, NULL);
-  for(tdll=list;tdll!=NULL;tdll=tdll->next)
-    ((double *)(out->array))[--i]=tdll->v;
+  if(num)
+    {
+      i=num;
+      out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &num, NULL, 0,
+                         minmapsize, NULL, NULL, NULL);
+      for(tdll=list;tdll!=NULL;tdll=tdll->next)
+        ((double *)(out->array))[--i]=tdll->v;
+    }
+  else
+    {
+      /* It is not possible to allocate a dataset with a size of 0 along
+         any dimension (in C it's possible, but conceptually it isn't). So,
+         we'll allocate space for one element, then free it. */
+      i=1;
+      out=gal_data_alloc(NULL, GAL_TYPE_FLOAT64, 1, &i, NULL, 0,
+                         minmapsize, NULL, NULL, NULL);
+      out->size=out->dsize[0]=0;
+      free(out->array);
+      out->array=NULL;
+    }
 
 
   /* Clean up and return. */
diff --git a/tests/during-dev.sh b/tests/during-dev.sh
index 578155c..36f6f26 100755
--- a/tests/during-dev.sh
+++ b/tests/during-dev.sh
@@ -70,7 +70,7 @@
 # space characters in them, quote the full value
 numjobs=8
 builddir=build
-outdir=~/desktop
+outdir=
 
 
 # Set the utility name, along with its arguments and options. NOTE, for
@@ -80,9 +80,9 @@ outdir=~/desktop
 # script, and once for the utility. In such cases it might be easier to
 # just add the argument/option to the final script that runs the utility
 # rather than these variables.
-utilname=crop
-arguments=/home/mohammad/documents/personal/research/software/local/c/gnuastro/build/tests/mkprofcat1.fits
-options="--section=-10:*+10,:250 --output=crop_section.fits --numthreads=1 
--mode=img -h0"
+utilname=
+arguments=
+options=
 
 
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]