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()