docs: def get_source_models(oqparam, gsim_lt, source_model_lt, monitor,
                        in_memory=True, srcfilter=None):
      """
      Build all the source models generated by the logic tree.
  
      :param oqparam:
          an :class:`openquake.commonlib.oqvalidation.OqParam` instance
      :param gsim_lt:
          a :class:`openquake.commonlib.logictree.GsimLogicTree` instance
      :param source_model_lt:
          a :class:`openquake.commonlib.logictree.SourceModelLogicTree` instance
      :param monitor:
          a `openquake.baselib.performance.Monitor` instance
      :param in_memory:
          if True, keep in memory the sources, else just collect the TRTs
      :param srcfilter:
          a SourceFilter instance with an .filename pointing to the cache file
      :returns:
          an iterator over :class:`openquake.commonlib.logictree.LtSourceModel`
          tuples
      """
      make_sm = SourceModelFactory()
      spinning_off = oqparam.pointsource_distance == {'default': 0.0}
      if spinning_off:
          logging.info('Removing nodal plane and hypocenter distributions')
      dist = 'no' if os.environ.get('OQ_DISTRIBUTE') == 'no' else 'processpool'
      smlt_dir = os.path.dirname(source_model_lt.filename)
      converter = sourceconverter.SourceConverter(
          oqparam.investigation_time,
          oqparam.rupture_mesh_spacing,
          oqparam.complex_fault_mesh_spacing,
          oqparam.width_of_mfd_bin,
          oqparam.area_source_discretization,
          oqparam.minimum_magnitude,
          not spinning_off,
          oqparam.source_id)
      if oqparam.calculation_mode.startswith('ucerf'):
          [grp] = nrml.to_python(oqparam.inputs["source_model"], converter)
      elif in_memory:
          logging.info('Reading the source model(s) in parallel')
          smap = parallel.Starmap(
              nrml.read_source_models, monitor=monitor, distribute=dist)
          for sm in source_model_lt.gen_source_models(gsim_lt):
              for name in sm.names.split():
                  fname = os.path.abspath(os.path.join(smlt_dir, name))
                  smap.submit([fname], converter)
          dic = {sm.fname: sm for sm in smap}
  
      # consider only the effective realizations
      nr = 0
      idx = 0
      grp_id = 0
      if monitor.hdf5:
          sources = hdf5.create(monitor.hdf5, 'source_info', source_info_dt)
          hdf5.create(monitor.hdf5, 'source_geom', point3d)
          filename = (getattr(srcfilter, 'filename', None)
                      if oqparam.prefilter_sources == 'no' else None)
      source_ids = set()
      for sm in source_model_lt.gen_source_models(gsim_lt):
          apply_unc = functools.partial(
              source_model_lt.apply_uncertainties, sm.path)
          src_groups = []
          for name in sm.names.split():
              fname = os.path.abspath(os.path.join(smlt_dir, name))
              if oqparam.calculation_mode.startswith('ucerf'):
                  sg = copy.copy(grp)
                  sg.id = grp_id
                  src = sg[0].new(sm.ordinal, sm.names)  # one source
                  source_ids.add(src.source_id)
                  src.src_group_id = grp_id
                  src.id = idx
                  if oqparam.number_of_logic_tree_samples:
                      src.samples = sm.samples
                  sg.sources = [src]
                  src_groups.append(sg)
                  idx += 1
                  grp_id += 1
                  data = [((sg.id, src.source_id, src.code, 0, 0,
                           src.num_ruptures, 0, 0, 0))]
                  hdf5.extend(sources, numpy.array(data, source_info_dt))
              elif in_memory:
                  newsm = make_sm(fname, dic[fname], apply_unc,
                                  oqparam.investigation_time)
                  for sg in newsm:
                      nr += sum(src.num_ruptures for src in sg)
                      # sample a source for each group
                      if os.environ.get('OQ_SAMPLE_SOURCES'):
                          sg.sources = random_filtered_sources(
                              sg.sources, srcfilter, sg.id + oqparam.random_seed)
                      for src in sg:
                          source_ids.add(src.source_id)
                          src.src_group_id = grp_id
                          src.id = idx
                          idx += 1
                      sg.id = grp_id
                      grp_id += 1
                      src_groups.append(sg)
                  if monitor.hdf5:
                      store_sm(newsm, filename, monitor)
              else:  # just collect the TRT models
                  groups = logictree.read_source_groups(fname)
                  for group in groups:
                      source_ids.update(src['id'] for src in group)
                  src_groups.extend(groups)
  
          if grp_id >= TWO16:
              # the limit is really needed only for event based calculations
              raise ValueError('There is a limit of %d src groups!' % TWO16)
  
          for brid, srcids in source_model_lt.info.applytosources.items():
              for srcid in srcids:
                  if srcid not in source_ids:
                      raise ValueError(
                          'The source %s is not in the source model, please fix '
                          'applyToSources in %s or the source model' %
                          (srcid, source_model_lt.filename))
          num_sources = sum(len(sg.sources) for sg in src_groups)
          sm.src_groups = src_groups
          trts = [mod.trt for mod in src_groups]
          source_model_lt.tectonic_region_types.update(trts)
          logging.info(
              'Processed source model %d with %d gsim path(s) and %d '
              'sources', sm.ordinal + 1, sm.num_gsim_paths, num_sources)
  
          gsim_file = oqparam.inputs.get('gsim_logic_tree')
          if gsim_file:  # check TRTs
              for src_group in src_groups:
                  if src_group.trt not in gsim_lt.values:
                      raise ValueError(
                          "Found in %r a tectonic region type %r inconsistent "
                          "with the ones in %r" % (sm, src_group.trt, gsim_file))
          yield sm
  
      logging.info('The composite source model has {:,d} ruptures'.format(nr))
  
      # log if some source file is being used more than once
      dupl = 0
      for fname, hits in make_sm.fname_hits.items():
          if hits > 1:
              logging.info('%s has been considered %d times', fname, hits)
              if not make_sm.changes:
                  dupl += hits
      if (dupl and not oqparam.optimize_same_id_sources and
              not oqparam.is_event_based()):
          logging.warning(
              'You are doing redundant calculations: please make sure '
              'that different sources have different IDs and set '
              'optimize_same_id_sources=true in your .ini file')
      if make_sm.changes:
          logging.info('Applied %d changes to the composite source model',
                       make_sm.changes)