docs: def get_mesh(oqparam):
      """
      Extract the mesh of points to compute from the sites,
      the sites_csv, or the region.
  
      :param oqparam:
          an :class:`openquake.commonlib.oqvalidation.OqParam` instance
      """
      global pmap, exposure, gmfs, eids
      if 'exposure' in oqparam.inputs and exposure is None:
          # read it only once
          exposure = get_exposure(oqparam)
      if oqparam.sites:
          return geo.Mesh.from_coords(oqparam.sites)
      elif 'sites' in oqparam.inputs:
          fname = oqparam.inputs['sites']
          header = get_csv_header(fname)
          if 'lon' in header:
              data = []
              for i, row in enumerate(
                      csv.DictReader(open(fname, encoding='utf-8-sig'))):
                  if header[0] == 'site_id' and row['site_id'] != str(i):
                      raise InvalidFile('%s: expected site_id=%d, got %s' % (
                          fname, i, row['site_id']))
                  data.append(' '.join([row['lon'], row['lat']]))
          elif 'gmfs' in oqparam.inputs:
              raise InvalidFile('Missing header in %(sites)s' % oqparam.inputs)
          else:
              data = [line.replace(',', ' ')
                      for line in open(fname, encoding='utf-8-sig')]
          coords = valid.coordinates(','.join(data))
          start, stop = oqparam.sites_slice
          c = (coords[start:stop] if header[0] == 'site_id'
               else sorted(coords[start:stop]))
          return geo.Mesh.from_coords(c)
      elif 'hazard_curves' in oqparam.inputs:
          fname = oqparam.inputs['hazard_curves']
          if isinstance(fname, list):  # for csv
              mesh, pmap = get_pmap_from_csv(oqparam, fname)
          elif fname.endswith('.xml'):
              mesh, pmap = get_pmap_from_nrml(oqparam, fname)
          else:
              raise NotImplementedError('Reading from %s' % fname)
          return mesh
      elif 'gmfs' in oqparam.inputs:
          eids, gmfs = _get_gmfs(oqparam)  # sets oqparam.sites
          return geo.Mesh.from_coords(oqparam.sites)
      elif oqparam.region_grid_spacing:
          if oqparam.region:
              poly = geo.Polygon.from_wkt(oqparam.region)
          elif 'site_model' in oqparam.inputs:
              sm = get_site_model(oqparam)
              poly = geo.Mesh(sm['lon'], sm['lat']).get_convex_hull()
          elif exposure:
              poly = exposure.mesh.get_convex_hull()
          else:
              raise InvalidFile('There is a grid spacing but not a region, '
                                'nor a site model, nor an exposure in %s' %
                                oqparam.inputs['job_ini'])
          try:
              mesh = poly.dilate(oqparam.region_grid_spacing).discretize(
                  oqparam.region_grid_spacing)
              return geo.Mesh.from_coords(zip(mesh.lons, mesh.lats))
          except Exception:
              raise ValueError(
                  'Could not discretize region with grid spacing '
                  '%(region_grid_spacing)s' % vars(oqparam))
      elif 'exposure' in oqparam.inputs:
          return exposure.mesh