def main():
    """ The main function of the module.  It reads input parameters
    and runs the time-advancing loop. """

    # == READ PARAMETERS ==
    parser = argparse.ArgumentParser()
    parser.add_argument("input", help="Parameter input file")
    parser.add_argument("-o", "--output", help="Output file", default=None)
    args = parser.parse_args()
    ofile = args.output or (splitext(args.input)[0] + '.h5')

    params = Parameters()
    params.file_load(args.input)

    plugins = [import_object(s)(params) for s in params.plugins]

    
    # == INIT THE SIMULATION INSTANCE ==
    box = CylBox(0, params.r * co.kilo, 
                 params.z0 * co.kilo, 
                 params.z1 * co.kilo)

    dim = CylDimensions(params.r_cells, params.z_cells)

    z = linspace(params.z0 * co.kilo, params.z1 * co.kilo, dim.nz + 1)

    sim = CylindricalLangevin(box, dim)

    # == LOAD FILES ==
    sim.load_ngas(os.path.join(params.input_dir, params.gas_density_file))
    sim.load_ne(os.path.join(params.input_dir, params.electron_density_file),
                cutoff=params.electron_density_cutoff * co.centi**-3)
    sim.load_ionization(os.path.join(params.input_dir, 
                                     params.ionization_rate_file))
    sim.set_mun(params.mu_N)


    # == SET BOUNDARY CONDITIONS ==
    flt = ones((2, 2))
    if params.lower_boundary != 0:
        # Set the lower boundary as a perfect conductor:
        flt[Z_, 0] = False
        
        # We also have to set a Neumann bc in hphi for a conductor
        sim.hphi.neumann_axes.append((Z_, 0, 1))

    
    flt[R, 0] = False
        
    sim.add_cpml_boundaries(params.ncpml, filter=flt)
    

    # == SET SOURCE ==
    # We set the sources by using a filter in x and y
    rsource = params.r_source * co.kilo
    z0source = params.z0_source * co.kilo
    z1source = params.z1_source * co.kilo

    r2 = sim.rf**2
    source_s = pi * sim.rf[r2 <= rsource**2][-1]**2
    source_zflt = logical_and(sim.zf[newaxis, :] <= z1source, 
                              sim.zf[newaxis, :] >= z0source)
    
    si0, si1 = [nonzero(source_zflt)[1][i] for i in (0, -1)]

    m = params.tau_f / params.tau_r
    

    # == PREPARE H5DF OUTPUT ==
    fp = h5py.File(ofile, 'w')
    params.h5_dump(fp)
    sim.save_global(fp)

    gsteps = fp.create_group('steps')
    gtrack = fp.create_group('track')


    # == PREPARE THE MAIN LOOP ==
    sim.set_dt(params.dt)
    sim.dens_update_lower = params.dens_update_lower * co.kilo
    insteps = 10
    nsave = int(params.output_dt / (insteps * params.dt))
    t = 0
    for p in plugins:
        p.initialize(sim)

    # == THE MAIN LOOP ==
    for i in range(int(params.end_t / (insteps * params.dt))):
        with ContextTimer("t = %f ms" % (t / co.milli)):
            for j in range(insteps):
                sim.update_e()
                for p in plugins:
                    p.update_e(sim)

                sim.update_h()
                sim.j[:, si0:si1 + 1, Z_] = \
                    (exp(-r2/rsource**2) 
                     * peak(sim.th, params.Q / source_s, 
                            params.tau_f, m))[:, newaxis]
                for p in plugins:
                    p.update_h(sim)

                t += params.dt
        
        # Save a coarser grid with higher time resolution
        if len(params.track_r) > 0 and len(params.track_z) > 0:
            step = "%.6d" % i
            g = gtrack.create_group(step)
            sim.track(g, params.track_r * co.kilo, params.track_z * co.kilo)
            fp.flush()
            
        if 0 == ((i - 1) % nsave):
            with ContextTimer("Saving step %d" % (i / nsave)):
                step = "%.4d" % (i / nsave)

                g = gsteps.create_group(step)
                sim.save(g)
                for p in plugins:
                    p.save(sim, g)

                fp.flush()