docs: def cast(self, new_model_name, new_channels, output_dir=None, clobber=False,
          **kwargs):
          """
          Cast a new model from this model. The new model might have a restricted
          wavelength range, different channels, larger sampling/binning, and/or a
          lower spectral resolution.
          """
  
          output_dir = output_dir if output_dir is not None else os.getcwd()
          output_prefix = os.path.join(output_dir, new_model_name)
  
          # Validation.
          if not isinstance(new_channels, dict):
              raise TypeError("channels must be a dictionary object")
  
          for name, descr in new_channels.iteritems():
              if not isinstance(descr, (tuple, list, np.ndarray)) \
              or len(descr) != 2:
                  raise TypeError("channel dictionary values must be a "\
                      "(wavelength_array, spectral_resolution)")
  
              new_wavelengths, spectral_resolution = descr
              if not isinstance(new_wavelengths, (list, np.ndarray)):
                  raise TypeError("wavelengths for the new channel {} must be an "
                      "array".format(name))
              if 2 > len(new_wavelengths):
                  raise ValueError("wavelength array for the new channel {0} only"
                      " contains {1} points".format(name, len(new_wavelengths)))
              if 0 > spectral_resolution:
                  raise ValueError("spectral resolution for the new channel {} "
                      "must be a positive value".format(name))
  
          # Clobber.
          if not clobber:
              # Check to make sure the output files won't exist already.
              output_suffixes = (".yaml", ".pkl", "-wavelengths.memmap",
                  "-intensities.memmap")
              for path in [output_prefix + suffix for suffix in output_suffixes]:
                  if os.path.exists(path):
                      raise IOError("output filename {} already exists"\
                          .format(path))
  
          # Sort the proposed channel names from blue to red wavelengths.
          channel_names = []
          min_wavelengths = []
          for name, (wavelengths, resolution) in new_channels.iteritems():
              channel_names.append(name)
              min_wavelengths.append(np.min(wavelengths))
  
          channel_names = np.array(channel_names)[np.argsort(min_wavelengths)]
  
          # Get the wavelengths.
          channel_wavelengths = []
          wavelength_range = [self.wavelengths.min(), self.wavelengths.max()]
          for name in channel_names:
  
              new_wavelength = np.sort(new_channels[name][0])
              i = np.searchsorted(new_wavelength, wavelength_range)
              channel_wavelengths.append(new_wavelength[i[0]:i[1]])
  
              if i[0] != 0 or i[1] != len(new_wavelength):
                  logger.warn("Clipping requested wavelength range in channel {0}"
                      " from {1:.0f}-{2:.0f} A to {3:.0f}-{4:.0f} A because the "
                      "region requested was not covered by the original model."\
                      .format(name, new_wavelength[0], new_wavelength[-1],
                          new_wavelength[i[0]], new_wavelength[i[1]]))
  
          channel_wavelengths = [new_channels[n][0] for n in channel_names]
          channel_sizes = [len(_) for _ in channel_wavelengths]
          num_pixels = sum(channel_sizes)
  
          # Find out which of the new channels overlap with the existing channels.
          channel_matches = {}
          for name, wavelengths in zip(channel_names, channel_wavelengths):
  
              overlapping_old_channels = self._overlapping_channels(wavelengths)
  
              # if there are no overlaps, do we have a problem?
              if len(overlapping_old_channels) == 0:
                  raise ValueError("no channels overlapping")
  
              elif len(overlapping_old_channels) > 1:
                  raise ValueError("too many channels overlapping!")
  
              channel_matches[name] = overlapping_old_channels[0]
  
          # Create the memory-mapped wavelength file.
          wavelengths_memmap = np.memmap(output_prefix + "-wavelengths.memmap",
              dtype="float32", mode="w+", shape=(num_pixels, ))
          wavelengths_memmap[:] = np.hstack(channel_wavelengths)
          wavelengths_memmap.flush()
          del wavelengths_memmap
  
          # Write the new configuration to file.
          with open(output_prefix + ".yaml", "w") as fp:
              header = "\n".join([
                  "# Model created on {0} from previous model with hash {1}"\
                      .format(strftime("%Y-%m-%d %H:%M:%S"), self.hash),
                  "# Grid parameters: {0}".format(
                      ", ".join(self.grid_points.dtype.names)),
                  "# Channel names: {0}".format(
                      ", ".join(channel_names))
                  ])
              configuration = self._configuration.copy()
              configuration.update({
                  "model_grid": {
                      "grid_points": output_prefix + ".pkl",
                      "wavelengths": output_prefix + "-wavelengths.memmap",
                      "intensities": output_prefix + "-intensities.memmap"
                  }
              })
              fp.write(header + "\n" + yaml.safe_dump(configuration, stream=None, 
                  allow_unicode=True, default_flow_style=False))
  
          # Create the pickled model file, with meta data.
          meta = self.meta.copy()
          meta.update({
              "channel_names": channel_names,
              "channel_sizes": channel_sizes,
              "channel_resolutions": [new_channels[n][1] for n in channel_names]
          })
          with open(output_prefix + ".pkl", "wb") as fp:
              pickle.dump((self.grid_points, meta), fp, -1)
              
          # Create the rebinning matrices.
          convolutions = []
          wavelength_indices = []
          fast_binning \
              = self._configuration.get("settings", {}).get("fast_binning", False)
          for name in channel_names:
              new_wavelengths, spectral_resolution = new_channels[name]
  
              indices = np.clip(self.wavelengths.searchsorted(
                  [new_wavelengths.min(), new_wavelengths.max()]) + [0, 1],
                  0, self.wavelengths.size - 1)
              wavelength_indices.append(indices)
              old_wavelengths = self.wavelengths[indices[0]:indices[1]]
              logger.debug("Casting {0} channel from [{1:.0f}, {2:.0f}] to "\
                  "[{3:.0f}, {4:.0f}]".format(name,
                      old_wavelengths[0], old_wavelengths[-1],
                      new_wavelengths[0], new_wavelengths[-1]))
  
              if fast_binning:
                  if spectral_resolution is None \
                  or not np.isfinite(spectral_resolution):
                      convolutions.append(lambda nw, ow, of: \
                          np.interp(nw, ow, of, left=np.nan, right=np.nan))
  
                  else:
                      print("OK")
                      logger.debug("Using fast binning with spectral resolution")
                      R_scale = 2.3548200450309493 * new_wavelengths.mean()**2/ np.diff(new_wavelengths).mean()
                      convolutions.append(lambda nw, ow, of: \
                          np.interp(nw, ow,
                              gaussian_filter1d(of, R_scale/spectral_resolution**2),
                              left=np.nan, right=np.nan))
  
              else:
                  if spectral_resolution is None \
                  or not np.isfinite(spectral_resolution):
                      convolutions.append(lambda *_: _[2] * specutils.sample.resample(
                          old_wavelengths, new_wavelengths))
                  else:
                      convolutions.append(lambda *_: _[2] * \
                          specutils.sample.resample_and_convolve(old_wavelengths,
                              new_wavelengths, new_resolution=spectral_resolution))
              
          # Load the intensities.
          intensities = np.memmap(
              self._configuration["model_grid"]["intensities"],
              shape=(self.grid_points.size, self.wavelengths.size),
              mode="r", dtype="float32")
  
          # Create a new intensities grid.
          cast_intensities = np.memmap(
              output_prefix + "-intensities.memmap",
              shape=(self.grid_points.size, num_pixels),
              mode="w+", dtype="float32")
  
          n = self.grid_points.size
          increment = int(n / 100)
          progressbar = kwargs.pop("__progressbar", False)
          if progressbar:
              print("Casting {} model:".format(new_model_name))
  
          for i in xrange(n):
              if progressbar:
                  if (i % increment == 0):
                      sys.stdout.write("\r[{done}{not_done}] "
                          "{percent:3.0f}%".format(done="=" * int(i / increment),
                          not_done=" " * int((n - i)/ increment),
                          percent=100. * i/n))
                      sys.stdout.flush()
              else:
                  logger.debug("Recasting point {0}/{1}".format(i, n))
  
              for j, (size, convolution, indices) \
              in enumerate(zip(channel_sizes, convolutions, wavelength_indices)):
  
                  new_wavelengths, _ = new_channels[channel_names[j]]
                  old_wl_idxs = np.clip(self.wavelengths.searchsorted(
                      [new_wavelengths.min(), new_wavelengths.max()]) + [0, 1],
                      0, self.wavelengths.size - 1)
                  old_wavelengths = self.wavelengths[old_wl_idxs[0]:old_wl_idxs[1]]
  
                  old_fluxes = np.copy(intensities[i, indices[0]:indices[1]])
  
                  idx = sum(channel_sizes[:j])
                  cast_intensities[i, idx:idx + size] = convolution(new_wavelengths, old_wavelengths, old_fluxes)
  
          """
          for j, (size, matrix, indices) \
          in enumerate(zip(channel_sizes, convolutions, wavelength_indices)):
              idx = sum(channel_sizes[:j])
              cast_intensities[:, idx:idx + size] = \
                  intensities[:, indices[0]:indices[1]] * matrix
          """
  
          cast_intensities.flush()
          del cast_intensities
          if progressbar:
              print("\r")
  
          return True