docs: def generate_network(nl_model, 
                       handler, 
                       seed=1234, 
                       always_include_props=False,
                       include_connections=True,
                       include_inputs=True,
                       base_dir=None):
      """
      Generate the network model as described in NeuroMLlite in a specific handler,
      e.g. NeuroMLHandler, PyNNHandler, etc.
      """
      
      pop_locations = {}
      cell_objects = {}
      synapse_objects = {}
      
      print_v("Starting net generation for %s%s..." % (nl_model.id, 
                                  ' (base dir: %s)' % base_dir if base_dir else ''))
      rng = random.Random(seed)
      
      if nl_model.network_reader:
          
          exec('from neuromllite.%s import %s' % (nl_model.network_reader.type, nl_model.network_reader.type))
          
          exec('network_reader = %s()' % (nl_model.network_reader.type))
          network_reader.parameters = nl_model.network_reader.parameters
          
          network_reader.parse(handler)
          pop_locations = network_reader.get_locations()
          
      else:
          notes = "Generated network: %s" % nl_model.id
          notes += "\n    Generation seed: %i" % (seed)
          if nl_model.parameters:
              notes += "\n    NeuroMLlite parameters: " 
              for p in nl_model.parameters:
                  notes += "\n        %s = %s" % (p, nl_model.parameters[p])
          handler.handle_document_start(nl_model.id, notes)
          temperature = '%sdegC' % nl_model.temperature if nl_model.temperature else None 
          handler.handle_network(nl_model.id, nl_model.notes, temperature=temperature)
          
      nml2_doc_temp = _extract_pynn_components_to_neuroml(nl_model)
      
      for c in nl_model.cells:
          
          if c.neuroml2_source_file:
              from pyneuroml import pynml
              nml2_doc = pynml.read_neuroml2_file(_locate_file(c.neuroml2_source_file, base_dir), 
                                                  include_includes=True)
              cell_objects[c.id] = nml2_doc.get_by_id(c.id)
              
          if c.pynn_cell:
              cell_objects[c.id] = nml2_doc_temp.get_by_id(c.id)
              
              
      for s in nl_model.synapses:
          if s.neuroml2_source_file:
              from pyneuroml import pynml
              nml2_doc = pynml.read_neuroml2_file(_locate_file(s.neuroml2_source_file, base_dir), 
                                                  include_includes=True)
              synapse_objects[s.id] = nml2_doc.get_by_id(s.id)
              
          if s.pynn_synapse:
              synapse_objects[s.id] = nml2_doc_temp.get_by_id(s.id)
              
              
      for p in nl_model.populations:
          
          size = evaluate(p.size, nl_model.parameters)
          properties = p.properties if p.properties else {}
          if p.random_layout:
              properties['region'] = p.random_layout.region
              
          if not p.random_layout and not p.single_location and not always_include_props:
              
              # If there are no positions (abstract network), and <property>
              # is added to <population>, jLems doesn't like it... (it has difficulty 
              # interpreting pop0[0]/v, etc.)
              # So better not to give properties...
              properties = {} 
              
          if p.notes:
              handler.handle_population(p.id, 
                                        p.component, 
                                        size, 
                                        cell_objects[p.component] if p.component in cell_objects else None,
                                        properties=properties,
                                        notes=p.notes)
          else:
              handler.handle_population(p.id, 
                                        p.component, 
                                        size, 
                                        cell_objects[p.component] if p.component in cell_objects else None,
                                        properties=properties)
                                   
          pop_locations[p.id] = np.zeros((size, 3))
          
          for i in range(size):
              if p.random_layout:
                  region = nl_model.get_child(p.random_layout.region, 'regions')
                  
                  x = region.x + rng.random() * region.width
                  y = region.y + rng.random() * region.height
                  z = region.z + rng.random() * region.depth
                  pop_locations[p.id][i] = (x, y, z)
  
                  handler.handle_location(i, p.id, p.component, x, y, z)
                  
              if p.single_location:
                  loc = p.single_location.location
                  x = loc.x
                  y = loc.y
                  z = loc.z
                  pop_locations[p.id][i] = (x, y, z)
  
                  handler.handle_location(i, p.id, p.component, x, y, z)
                  
                  
          if hasattr(handler, 'finalise_population'):
              handler.finalise_population(p.id)
          
      if include_connections:
          for p in nl_model.projections:
  
              type = p.type if p.type else 'projection'
  
              handler.handle_projection(p.id, 
                                        p.presynaptic, 
                                        p.postsynaptic, 
                                        p.synapse,
                                        synapse_obj=synapse_objects[p.synapse] if p.synapse in synapse_objects else None,
                                        pre_synapse_obj=synapse_objects[p.pre_synapse] if p.pre_synapse in synapse_objects else None,
                                        type=type)
  
              delay = p.delay if p.delay else 0
              weight = p.weight if p.weight else 1
  
              conn_count = 0
              if p.random_connectivity:
                  for pre_i in range(len(pop_locations[p.presynaptic])):
                      for post_i in range(len(pop_locations[p.postsynaptic])):
                          flip = rng.random()
                          #print("Is cell %i conn to %i, prob %s - %s"%(pre_i, post_i, flip, p.random_connectivity.probability))
                          if flip < p.random_connectivity.probability:
                              weight = evaluate(weight, nl_model.parameters)
                              delay = evaluate(delay, nl_model.parameters)
                              #print_v("Adding connection %i with weight: %s, delay: %s"%(conn_count, weight, delay))
                              handler.handle_connection(p.id, 
                                                        conn_count, 
                                                        p.presynaptic, 
                                                        p.postsynaptic, 
                                                        p.synapse, \
                                                        pre_i, \
                                                        post_i, \
                                                        preSegId=0, \
                                                        preFract=0.5, \
                                                        postSegId=0, \
                                                        postFract=0.5, \
                                                        delay=delay, \
                                                        weight=weight)
                              conn_count += 1
                              
              if p.convergent_connectivity:
                  
                  for post_i in range(len(pop_locations[p.postsynaptic])):
                  
                      for count in range(int(p.convergent_connectivity.num_per_post)):
                          found = False
                          while not found:
                              pre_i = int(rng.random()*len(pop_locations[p.presynaptic]))
                              if p.presynaptic==p.postsynaptic and pre_i==post_i:
                                  found=False
                              else:
                                  found=True
  
                          weight = evaluate(weight, nl_model.parameters)
                          delay = evaluate(delay, nl_model.parameters)
                          print_v("Adding connection %i (%i->%i; %i to %s of post) with weight: %s, delay: %s"%(conn_count, pre_i, post_i, count, p.convergent_connectivity.num_per_post, weight, delay))
                          handler.handle_connection(p.id, 
                                                    conn_count, 
                                                    p.presynaptic, 
                                                    p.postsynaptic, 
                                                    p.synapse, \
                                                    pre_i, \
                                                    post_i, \
                                                    preSegId=0, \
                                                    preFract=0.5, \
                                                    postSegId=0, \
                                                    postFract=0.5, \
                                                    delay=delay, \
                                                    weight=weight)
                          conn_count += 1
  
              elif p.one_to_one_connector:
                  for i in range(min(len(pop_locations[p.presynaptic]), len(pop_locations[p.postsynaptic]))):
                      weight = evaluate(weight, nl_model.parameters)
                      delay = evaluate(delay, nl_model.parameters)
                      #print_v("Adding connection %i with weight: %s, delay: %s"%(conn_count, weight, delay))
                      handler.handle_connection(p.id, 
                                                conn_count, 
                                                p.presynaptic, 
                                                p.postsynaptic, 
                                                p.synapse, \
                                                i, \
                                                i, \
                                                preSegId=0, \
                                                preFract=0.5, \
                                                postSegId=0, \
                                                postFract=0.5, \
                                                delay=delay, \
                                                weight=weight)
                      conn_count += 1
  
              handler.finalise_projection(p.id, 
                                          p.presynaptic, 
                                          p.postsynaptic, 
                                          p.synapse)
                                   
      if include_inputs:   
          for input in nl_model.inputs:
  
              handler.handle_input_list(input.id, 
                                        input.population, 
                                        input.input_source, 
                                        size=0, 
                                        input_comp_obj=None)
  
              input_count = 0      
              for i in range(len(pop_locations[input.population])):
                  flip = rng.random()
                  weight = input.weight if input.weight else 1
                  if flip * 100. < input.percentage:
                      
                      number_per_cell = evaluate(input.number_per_cell, nl_model.parameters) if input.number_per_cell else 1
                      
                      for j in range(number_per_cell):
                          handler.handle_single_input(input.id, 
                                                      input_count, 
                                                      i,
                                                      weight=evaluate(weight, nl_model.parameters))
                          input_count += 1
  
              handler.finalise_input_source(input.id)
              
      if hasattr(handler, 'finalise_document'):
          handler.finalise_document()