def traction_test(ell=0.05,
                  ell_e=.1,
                  degree=1,
                  n=3,
                  nu=0.,
                  load_min=0,
                  load_max=2,
                  loads=None,
                  nsteps=20,
                  Lx=1.,
                  Ly=0.1,
                  outdir="outdir",
                  postfix='',
                  savelag=1,
                  sigma_D0=1.,
                  periodic=False,
                  continuation=False,
                  checkstability=True,
                  configString='',
                  test=True):
    # constants
    # ell = ell
    Lx = Lx
    load_min = load_min
    load_max = load_max
    nsteps = nsteps
    outdir = outdir
    loads = loads

    savelag = 1
    nu = dolfin.Constant(nu)
    ell = dolfin.Constant(ell)
    ell_e = ell_e
    E = dolfin.Constant(1.0)
    K = E.values()[0] / ell_e**2.
    sigma_D0 = E
    n = n
    # h = ell.values()[0]/n
    h = max(ell.values()[0] / n, .005)
    cell_size = h
    continuation = continuation
    isPeriodic = periodic
    config = json.loads(configString) if configString != '' else ''

    cmd_parameters = {
        'material': {
            "ell": ell.values()[0],
            "ell_e": ell_e,
            "K": K,
            "E": E.values()[0],
            "nu": nu.values()[0],
            "sigma_D0": sigma_D0.values()[0]
        },
        'geometry': {
            'Lx': Lx,
            'Ly': Ly,
            'n': n,
        },
        'experiment': {
            'test': test,
            'periodic': isPeriodic,
            'signature': ''
        },
        'stability': {
            'checkstability': checkstability,
            'continuation': continuation
        },
        'time_stepping': {
            'load_min': load_min,
            'load_max': load_max,
            'nsteps': nsteps,
            'outdir': outdir,
            'postfix': postfix,
            'savelag': savelag
        },
        'alt_min': {},
        "code": {}
    }

    # --------------------

    for par in parameters:
        parameters[par].update(cmd_parameters[par])

    if config:
        for par in config:
            parameters[par].update(config[par])
    # else:

    # parameters['material']['ell_e'] =

    Lx = parameters['geometry']['Lx']
    Ly = parameters['geometry']['Ly']
    ell = parameters['material']['ell']
    ell_e = parameters['material']['ell_e']

    BASE_DIR = os.path.dirname(os.path.realpath(__file__))
    fname = "film"
    print(BASE_DIR)
    os.path.isfile(fname)

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()

    if parameters['experiment']['test'] == True:
        outdir += '-{}'.format(cmd_parameters['time_stepping']['postfix'])
    else:
        outdir += '-{}{}'.format(signature,
                                 cmd_parameters['time_stepping']['postfix'])
    outdir = outdir + '-cont'
    parameters['time_stepping']['outdir'] = outdir
    Path(outdir).mkdir(parents=True, exist_ok=True)
    print('Outdir is: ' + outdir)

    with open(os.path.join(outdir, 'rerun.sh'), 'w') as f:
        configuration = deepcopy(parameters)
        configuration['time_stepping'].pop('outdir')
        str(configuration).replace("\'True\'",
                                   "True").replace("\'False\'", "False")
        rerun_cmd = 'python3 {} --config="{}"'.format(
            os.path.basename(__file__), configuration)
        f.write(rerun_cmd)

    with open(os.path.join(outdir, 'parameters.pkl'), 'w') as f:
        json.dump(parameters, f)

    with open(os.path.join(outdir, 'signature.md5'), 'w') as f:
        f.write(signature)
    print(parameters)

    # boundary_meshfunction = dolfin.MeshFunction("size_t", mesh, "meshes/%s-%s_facet_region.xml"%(fname, signature))
    # cells_meshfunction = dolfin.MeshFunction("size_t", mesh, "meshes/%s-%s_physical_region.xml"%(fname, signature))

    # ------------------
    geometry_parameters = parameters['geometry']

    geom_signature = hashlib.md5(
        str(geometry_parameters).encode('utf-8')).hexdigest()
    meshfile = "%s/meshes/%s-%s.xml" % (BASE_DIR, fname, geom_signature)
    # cmd_parameters['experiment']['signature']=signature

    if os.path.isfile(meshfile):
        print("Meshfile %s exists" % meshfile)
        mesh = dolfin.Mesh("meshes/%s-%s.xml" % (fname, geom_signature))
    else:
        print("Creating meshfile: %s" % meshfile)
        print(('DEBUG: (-Lx/2. ={} , -Ly/2.={})'.format(Lx / 2., -Ly / 2.)))
        geom = mshr.Rectangle(dolfin.Point(-Lx / 2., -Ly / 2.),
                              dolfin.Point(Lx / 2., Ly / 2.))
        mesh = mshr.generate_mesh(geom, n * int(float(Lx / ell)))
    print(meshfile)

    mesh_xdmf = dolfin.XDMFFile("meshes/%s-%s.xdmf" % (fname, geom_signature))
    mesh_xdmf.write(mesh)
    if rank == 0:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        meshf << mesh

    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    u = dolfin.Function(V_u, name="Total displacement")
    alpha = dolfin.Function(V_alpha, name="Damage")

    bcs_alpha = []
    bcs_u = [
        DirichletBC(V_u, Constant((0., 0)),
                    '(near(x[0], %f) or near(x[0], %f))' % (-Lx / 2., Lx / 2.))
    ]

    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    bottom = dolfin.CompiledSubDomain("near(x[1],-Ly/2.)", Ly=Ly)
    top = dolfin.CompiledSubDomain("near(x[1],Ly/2.)", Ly=Ly)

    mf = dolfin.MeshFunction("size_t", mesh, 1, 0)
    right.mark(mf, 1)
    left.mark(mf, 2)
    bottom.mark(mf, 3)

    state = [u, alpha]

    Z = dolfin.FunctionSpace(
        mesh, dolfin.MixedElement([u.ufl_element(),
                                   alpha.ufl_element()]))
    z = dolfin.Function(Z)

    v, beta = dolfin.split(z)
    dx = dolfin.Measure("dx", metadata=form_compiler_parameters, domain=mesh)
    ds = dolfin.Measure("ds", subdomain_data=mf)

    # Files for output
    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
    file_con = dolfin.XDMFFile(os.path.join(outdir, "continuation.xdmf"))
    file_bif = dolfin.XDMFFile(
        os.path.join(outdir, "bifurcation_postproc.xdmf"))

    for f in [file_out, file_eig, file_con, file_bif]:
        f.parameters["functions_share_mesh"] = True
        f.parameters["flush_output"] = True

    # Problem definition

    foundation_density = 1. / 2. * 1. / ell_e**2. * dot(u, u)
    model = DamagePrestrainedElasticityModel(
        state,
        E,
        nu,
        ell,
        sigma_D0,
        user_functional=foundation_density,
        eps0t=Expression([['t', 0.], [0., 0.]], t=0., degree=0))
    # import pdb; .set_trace()
    model.dx = dx
    model.ds = ds
    energy = model.total_energy_density(u, alpha) * dx
    # Alternate minimization solver
    solver = solvers.AlternateMinimizationSolver(
        energy, [u, alpha], [bcs_u, bcs_alpha],
        parameters=parameters['alt_min'])

    rP = model.rP(u, alpha, v, beta) * dx + 1 / ell_e**2. * dot(v, v) * dx
    rN = model.rN(u, alpha, beta) * dx

    stability = StabilitySolver(mesh,
                                energy, [u, alpha], [bcs_u, bcs_alpha],
                                z,
                                parameters=parameters['stability'])
    # stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z, parameters = parameters['stability'], rayleigh=[rP, rN])

    # if isPeriodic:
    #     stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z,
    #         parameters = stability_parameters,
    #         constrained_domain = PeriodicBoundary(Lx))
    # else:
    #     stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z, parameters = parameters['stability'])

    load_steps = np.linspace(load_min, load_max,
                             parameters['time_stepping']['nsteps'])
    if loads:
        load_steps = loads

    time_data = []

    linesearch = LineSearch(energy, [u, alpha])
    alpha_old = dolfin.Function(alpha.function_space())
    lmbda_min_prev = 0.000001
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_count = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    bifurcation_loads = []

    tot_energy = model.elastic_energy_density(model.eps(u), alpha)*dx + \
            1./2.*1/ell_e**2. * dot(u, u)*dx             + \
            model.damage_dissipation_density(alpha)*dx
    cont_atol = 1e-3

    for it, load in enumerate(load_steps):
        model.eps0t.t = load
        alpha_old.assign(alpha)
        ColorPrint.print_warn('Solving load t = {:.2f}'.format(load))

        # First order stability conditions
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions
        (stable, negev) = stability.solve(solver.problem_alpha.lb)
        ColorPrint.print_pass(
            'Current state is{}stable'.format(' ' if stable else ' un'))
        # import pdb; pdb.set_trace()

        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        # print('DEBUG: lmbda min', lmbda_min_prev)
        # print('DEBUG: mineig', mineig)
        Deltav = (mineig - lmbda_min_prev) if hasattr(stability, 'eigs') else 0

        if (mineig + Deltav) * (lmbda_min_prev +
                                dolfin.DOLFIN_EPS) < 0 and not bifurcated:
            bifurcated = True
            # save 3 bif modes
            print('DEBUG: About to bifurcate load ', load, 'step', it)
            bifurcation_loads.append(load)
            bifurc_count += 1

        lmbda_min_prev = mineig if hasattr(stability, 'mineig') else 0.
        if stable:
            solver.update()
        else:
            # Continuation
            iteration = 1
            energy_pre = dolfin.assemble(tot_energy)
            alpha_bif.assign(alpha)
            alpha_bif_old.assign(alpha_old)

            while stable == False and iteration < 30:
                # linesearch
                perturbation_v = stability.perturbation_v
                perturbation_beta = stability.perturbation_beta

                h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
                    [u, alpha, alpha_old], perturbation_v, perturbation_beta)

                # import pdb; pdb.set_trace()
                # if h_opt != 0:
                if h_opt > cont_atol:

                    save_current_bifurcation = True

                    # admissible
                    uval = u.vector()[:] + h_opt * perturbation_v.vector()[:]
                    aval = alpha.vector(
                    )[:] + h_opt * perturbation_beta.vector()[:]

                    u.vector()[:] = uval
                    alpha.vector()[:] = aval

                    u.vector().vec().ghostUpdate()
                    alpha.vector().vec().ghostUpdate()

                    (time_data_i, am_iter) = solver.solve()
                    (stable, negev) = stability.solve(alpha_old)
                    ColorPrint.print_pass(
                        '    Continuation iteration #{}, current state is{}stable'
                        .format(iteration, ' ' if stable else ' un'))
                    energy_post = dolfin.assemble(tot_energy)
                    ener_diff = energy_post - energy_pre
                    ColorPrint.print_warn(
                        'DEBUG: step {}, iteration {}, En_post - En_pre ={}'.
                        format(it, iteration, energy_post - energy_pre))

                    iteration += 1
                    if ener_diff < 0: bifurcated = False
                else:
                    # warn
                    ColorPrint.print_warn(
                        'DEBUG: Found (almost) zero increment, we are stuck in the matrix'
                    )
                    ColorPrint.print_warn('DEBUG:   h_opt = {}'.format(h_opt))
                    ColorPrint.print_warn('DEBUG: Continuing load program')
                    break

            solver.update()
            # stable == True
            # modes = np.where(stability.eigs < 0)[0]
            # with file_bif as file:
            #     leneigs = len(modes)
            #     maxmodes = min(3, leneigs)
            #     for n in range(maxmodes):
            #         mode = dolfin.project(stability.linsearch[n]['beta_n'], V_alpha)
            #         modename = 'beta-%d'%n
            #         print(modename)
            #         file.write_checkpoint(mode, modename, 0, append=True)

            # bifurc_count += 1
        time_data_i["load"] = load
        time_data_i["stable"] = stable
        time_data_i["dissipated_energy"] = dolfin.assemble(
            model.damage_dissipation_density(alpha) * dx)
        time_data_i["foundation_energy"] = dolfin.assemble(
            1. / 2. * 1 / ell_e**2. * dot(u, u) * dx)
        time_data_i["membrane_energy"] = dolfin.assemble(
            model.elastic_energy_density(model.eps(u), alpha) * dx)
        time_data_i["elastic_energy"] = time_data_i[
            "membrane_energy"] + time_data_i["foundation_energy"]
        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        # import pdb; pdb.set_trace()

        _sigma = model.stress(model.eps(u), alpha)
        e1 = dolfin.Constant([1, 0])
        _snn = dolfin.dot(dolfin.dot(_sigma, e1), e1)
        time_data_i["sigma"] = 1 / Ly * dolfin.assemble(_snn * model.ds(1))

        time_data_i["S(alpha)"] = dolfin.assemble(1. / (model.a(alpha)) *
                                                  model.dx)
        time_data_i["A(alpha)"] = dolfin.assemble((model.a(alpha)) * model.dx)
        time_data_i["avg_alpha"] = dolfin.assemble(alpha * model.dx)

        ColorPrint.print_pass(
            "Time step {:.4g}: it {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"], time_data_i["iterations"],
                time_data_i["alpha_error"]))

        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)

        if np.mod(it, savelag) == 0:
            with file_out as f:
                f.write(alpha, load)
                f.write(u, load)
                f.write_checkpoint(alpha,
                                   "alpha-{}".format(it),
                                   0,
                                   append=True)
                # with file_bif as f:
                print('DEBUG: written step ', it)

        if save_current_bifurcation:
            # modes = np.where(stability.eigs < 0)[0]

            time_data_i['h_opt'] = h_opt
            time_data_i['max_h'] = hmax
            time_data_i['min_h'] = hmin

            with file_bif as file:
                beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                file.write_checkpoint(beta0v,
                                      'beta0',
                                      bifurc_count - 1,
                                      append=True)
                file.write_checkpoint(alpha_bif_old,
                                      'alpha-old',
                                      bifurc_count - 1,
                                      append=True)
                file.write_checkpoint(alpha_bif,
                                      'alpha-bif',
                                      bifurc_count - 1,
                                      append=True)
                file.write_checkpoint(alpha,
                                      'alpha',
                                      bifurc_count - 1,
                                      append=True)

                np.save(os.path.join(outdir, 'energy_perturbations'),
                        energy_perturbations,
                        allow_pickle=True,
                        fix_imports=True)

            with file_eig as file:
                _v = dolfin.project(
                    dolfin.Constant(h_opt) * perturbation_v, V_u)
                _beta = dolfin.project(
                    dolfin.Constant(h_opt) * perturbation_beta, V_alpha)
                _v.rename('perturbation displacement',
                          'perturbation displacement')
                _beta.rename('perturbation damage', 'perturbation damage')
                # import pdb; pdb.set_trace()
                f.write(_v, load)
                f.write(_beta, load)
                file.write_checkpoint(_v,
                                      'perturbation_v',
                                      bifurc_count - 1,
                                      append=True)
                file.write_checkpoint(_beta,
                                      'perturbation_beta',
                                      bifurc_count - 1,
                                      append=True)

            save_current_bifurcation = False

        time_data_pd.to_json(os.path.join(outdir, "time_data.json"))

    plt.figure()
    plt.plot(time_data_pd["load"].values(),
             time_data_pd["iterations"].values(),
             label='its')
    plt.semilogy()
    ax = plt.gca()
    ax2 = ax.twinx()
    ax2.plot(time_data_pd["load"].values(),
             time_data_pd["alpha_error"].values(),
             'o',
             c='C1',
             label='alpha error')
    plt.savefig(os.path.join(outdir, 'am.pdf'))
    plt.legend()
    plt.close()
    # user_postprocess_timestep(alpha, parameters, load, xresol = 100)

    plt.figure()
    dolfin.plot(alpha)
    plt.savefig(os.path.join(outdir, "alpha.png"))
    plt.figure()
    dolfin.plot(u, mode="displacement")
    plt.savefig(os.path.join(outdir, "u.png"))
    _nu = parameters['material']['nu']
    _E = parameters['material']['E']
    _w1 = parameters['material']['sigma_D0']**2. / parameters['material']['E']

    tc = np.sqrt(2 * _w1 / (_E * (1. - 2. * _nu) * (1. + _nu)))
    if parameters['stability']['checkstability'] == 'True':
        pp.plot_spectrum(parameters, outdir, time_data_pd.sort_values('load'),
                         tc)
    # plt.show()
    print(time_data_pd)
    print()
    print('Output in: ' + outdir)

    return time_data_pd
def traction_test(
    ell=0.1,
    degree=1,
    n=3,
    nu=0.0,
    E=1.,
    load_min=0,
    load_max=2,
    loads=None,
    nsteps=20,
    Lx=1,
    Ly=0.1,
    outdir="outdir",
    postfix='',
    savelag=1,
    sigma_D0=1.,
    continuation=False,
    checkstability=True,
    configString=''
):
    # constants
    ell = ell
    Lx = Lx
    Ly = Ly
    load_min = load_min
    load_max = load_max
    nsteps = nsteps
    outdir = outdir
    loads=loads

    savelag = 1
    nu = dolfin.Constant(nu)
    ell = dolfin.Constant(ell)
    E0 = dolfin.Constant(E)
    sigma_D0 = E0
    n = n
    continuation = continuation
    config = json.loads(configString) if configString != '' else ''

    cmd_parameters =  {
    'material': {
        "ell": ell.values()[0],
        "E": E0.values()[0],
        "nu": nu.values()[0],
        "sigma_D0": sigma_D0.values()[0]},
    'geometry': {
        'Lx': Lx,
        'Ly': Ly,
        'n': n,
        },
    'experiment': {
        'signature': ''
        },
    'stability': {
        'checkstability' : checkstability,
        'continuation' : continuation
        },
    'time_stepping': {
        'load_min': load_min,
        'load_max': load_max,
        'nsteps':  nsteps,
        'outdir': outdir,
        'postfix': postfix,
        'savelag': savelag},
    'alt_min': {}, "code": {}
    }

    if config:
        for par in config: parameters[par].update(config[par])
    else:
        for par in parameters: parameters[par].update(cmd_parameters[par])
    print(parameters)

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir += '-{}{}'.format(signature, cmd_parameters['time_stepping']['postfix'])
    # outdir += '-{}'.format(cmd_parameters['time_stepping']['postfix'])
    parameters['time_stepping']['outdir']=outdir
    Path(outdir).mkdir(parents=True, exist_ok=True)
    print('Outdir is: '+outdir)

    with open(os.path.join(outdir, 'rerun.sh'), 'w') as f:
        configuration = deepcopy(parameters)
        configuration['time_stepping'].pop('outdir')
        str(configuration).replace("\'True\'", "True").replace("\'False\'", "False")
        rerun_cmd = 'python3 {} --config="{}"'.format(__file__, configuration)
        f.write(rerun_cmd)

    with open(os.path.join(outdir, 'parameters.pkl'), 'w') as f:
        json.dump(parameters, f)

    with open(os.path.join(outdir, 'signature.md5'), 'w') as f:
        f.write(signature)


    geom = mshr.Rectangle(dolfin.Point(-Lx/2., -Ly/2.), dolfin.Point(Lx/2., Ly/2.))
    mesh = mshr.generate_mesh(geom,  int(float(n * Lx / ell)))
    meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
    meshf << mesh

    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    bottom = dolfin.CompiledSubDomain("near(x[1],-Ly/2.)", Ly=Ly)
    top = dolfin.CompiledSubDomain("near(x[1],Ly/2.)", Ly=Ly)
    left_bottom_pt = dolfin.CompiledSubDomain("near(x[0],-Lx/2.) && near(x[1],-Ly/2.)", Lx=Lx, Ly=Ly)

    mf = dolfin.MeshFunction("size_t", mesh, 1, 0)
    right.mark(mf, 1)
    left.mark(mf, 2)
    bottom.mark(mf, 3)
    ds = dolfin.Measure("ds", subdomain_data=mf)
    dx = dolfin.Measure("dx", metadata=form_compiler_parameters, domain=mesh)

    # Function Spaces
    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    u = dolfin.Function(V_u, name="Total displacement")
    alpha = dolfin.Function(V_alpha, name="Damage")
    state = [u, alpha]

    Z = dolfin.FunctionSpace(mesh, dolfin.MixedElement([u.ufl_element(),alpha.ufl_element()]))
    z = dolfin.Function(Z)

    v, beta = dolfin.split(z)

    # BCs (homogenous version needed for residual evaluation)
    ut = dolfin.Expression("t", t=0.0, degree=0)
    bcs_u = [dolfin.DirichletBC(V_u.sub(0), dolfin.Constant(0), left),
             dolfin.DirichletBC(V_u.sub(0), ut, right),
             dolfin.DirichletBC(V_u, (0, 0), left_bottom_pt, method="pointwise")]

    bcs_alpha = []

    # Files for output
    ColorPrint.print_warn('Outdir = {}'.format(outdir))
    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_out.parameters["functions_share_mesh"] = True
    file_out.parameters["flush_output"] = True
    file_con = dolfin.XDMFFile(os.path.join(outdir, "cont.xdmf"))
    file_con.parameters["functions_share_mesh"] = True
    file_con.parameters["flush_output"] = True
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "modes.xdmf"))
    file_eig.parameters["functions_share_mesh"] = True
    file_eig.parameters["flush_output"] = True
    file_postproc = dolfin.XDMFFile(os.path.join(outdir, "output_postproc.xdmf"))
    file_postproc.parameters["functions_share_mesh"] = True
    file_postproc.parameters["flush_output"] = True
    file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation_postproc.xdmf"))
    file_bif.parameters["functions_share_mesh"] = True
    file_bif.parameters["flush_output"] = True

    # Problem definition
    model = DamageElasticityModel(state, E0, nu, ell, sigma_D0)
    model.dx = dx
    model.ds = ds
    energy = model.total_energy_density(u, alpha)*model.dx

    # Alternate minimisation solver
    solver = solvers.AlternateMinimizationSolver(energy,
        [u, alpha], [bcs_u, bcs_alpha], parameters=parameters['alt_min'])

    rP = model.rP(u, alpha, v, beta)*model.dx
    rN = model.rN(u, alpha, beta)*model.dx

    stability = StabilitySolver(mesh, energy,
        [u, alpha], [bcs_u, bcs_alpha], z, rayleigh=[rP, rN], parameters = parameters['stability'])
    # stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z, parameters = parameters['stability'])

    # Time iterations
    load_steps = np.linspace(load_min, load_max, parameters['time_stepping']['nsteps'])

    if loads:
        load_steps = loads

    stability.parameters['checkstability'] = True

    time_data = []

    linesearch = LineSearch(energy, [u, alpha])
    alpha_old = dolfin.Function(alpha.function_space())
    lmbda_min_prev = 0.000001
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    for it, load in enumerate(load_steps):
        ut.t = load
        alpha_old.assign(alpha)

        ColorPrint.print_warn('Solving load t = {:.2f}'.format(load))

        # First order stability conditions
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions
        (stable, negev) = stability.solve(solver.problem_alpha.lb)
        ColorPrint.print_pass('Current state is{}stable'.format(' ' if stable else ' un'))


        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        print('lmbda min', lmbda_min_prev)
        print('mineig', mineig)
        Deltav = (mineig-lmbda_min_prev) if hasattr(stability, 'eigs') else 0

        if (mineig + Deltav)*(lmbda_min_prev+dolfin.DOLFIN_EPS) < 0 and not bifurcated:
            bifurcated = True

            # save 3 bif modes
            print('About to bifurcate load ', load, 'step', it)
            bifurcation_loads.append(load)
            print('DEBUG: decide what to do')
            # save_current_bifurcation = True
            bifurc_i += 1

        lmbda_min_prev = mineig if hasattr(stability, 'mineig') else 0.


        if stable:
            solver.update()
        else:
            # Continuation
            iteration = 1
            while stable == False:
                # linesearch
                perturbation_v    = stability.perturbation_v
                perturbation_beta = stability.perturbation_beta

                h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
                    [u, alpha, alpha_old],
                    perturbation_v, perturbation_beta)

                if h_opt != 0:
                    save_current_bifurcation = True
                    alpha_bif.assign(alpha)
                    alpha_bif_old.assign(alpha_old)
                    # admissible
                    uval = u.vector()[:]     + h_opt * perturbation_v.vector()[:]
                    aval = alpha.vector()[:] + h_opt * perturbation_beta.vector()[:]

                    u.vector()[:] = uval
                    alpha.vector()[:] = aval

                    u.vector().vec().ghostUpdate()
                    alpha.vector().vec().ghostUpdate()

                    # import pdb; pdb.set_trace()
                    (time_data_i, am_iter) = solver.solve()
                    (stable, negev) = stability.solve(alpha_old)
                    ColorPrint.print_pass('    Continuation iteration {}, current state is{}stable'.format(iteration, ' ' if stable else ' un'))
                    iteration += 1

                else:
                    # warn
                    ColorPrint.print_warn('Found zero increment, we are stuck in the matrix')
                    ColorPrint.print_warn('Continuing load program')
                    break

            solver.update()
            # stable == True    

        time_data_i["load"] = load
        time_data_i["stable"] = stable
        time_data_i["elastic_energy"] = dolfin.assemble(
            model.elastic_energy_density(model.eps(u), alpha)*dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            model.damage_dissipation_density(alpha)*dx)
        time_data_i["eigs"] = stability.eigs if hasattr(stability, 'eigs') else np.inf
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        # import pdb; pdb.set_trace()

        _sigma = model.stress(model.eps(u), alpha)
        e1 = dolfin.Constant([1, 0])
        _snn = dolfin.dot(dolfin.dot(_sigma, e1), e1)
        time_data_i["sigma"] = 1/Ly * dolfin.assemble(_snn*model.ds(1))

        time_data_i["S(alpha)"] = dolfin.assemble(1./(model.a(alpha))*model.dx)
        time_data_i["A(alpha)"] = dolfin.assemble((model.a(alpha))*model.dx)
        time_data_i["avg_alpha"] = 1/dolfin.assemble(dolfin.project(Constant(1.), V_alpha)*model.dx) * dolfin.assemble(alpha*model.dx)

        ColorPrint.print_pass(
            "Time step {:.4g}: it {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"],
                time_data_i["iterations"],
                time_data_i["alpha_error"]))

        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)

        if np.mod(it, savelag) == 0:
            with file_out as file:
                file.write(alpha, load)
                file.write(u, load)

            with file_postproc as file:
                file.write_checkpoint(alpha, "alpha-{}".format(it), 0, append = True)
                print('DEBUG: written step ', it)
                file.read_checkpoint(alpha, 'alpha-{}'.format(it), 0)
                print('DEBUG: read step {}, load {}'.format(it, load))
                print('DEBUG: file {}'.format(os.path.join(outdir, "output_postproc.xdmf")))
                # import pdb; pdb.set_trace()

        if save_current_bifurcation:
            # modes = np.where(stability.eigs < 0)[0]
            time_data_i['h_opt'] = h_opt
            time_data_i['max_h'] = hmax
            time_data_i['min_h'] = hmin

            with file_bif as file:
                # leneigs = len(modes)
                # maxmodes = min(3, leneigs)
                beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                print('DEBUG: irrev ', alpha.vector()-alpha_old.vector())
                file.write_checkpoint(beta0v, 'beta0', 0, append = True)
                file.write_checkpoint(alpha_bif_old, 'alpha-old', 0, append=True)
                file.write_checkpoint(alpha_bif, 'alpha-bif', 0, append=True)
                file.write_checkpoint(alpha, 'alpha', 0, append=True)

                np.save(os.path.join(outdir, 'energy_perturbations'), energy_perturbations, allow_pickle=True, fix_imports=True)

            with file_eig as file:
                _v = dolfin.project(dolfin.Constant(h_opt)*perturbation_v, V_u)
                _beta = dolfin.project(dolfin.Constant(h_opt)*perturbation_beta, V_alpha)
                _v.rename('perturbation displacement', 'perturbation displacement')
                _beta.rename('perturbation damage', 'perturbation damage')
                # import pdb; pdb.set_trace()
                file.write(_v, load)
                file.write(_beta, load)
                file.write_checkpoint(_v, 'perturbation_v', 0, append=True)
                file.write_checkpoint(_beta, 'perturbation_beta', 0, append=True)

            save_current_bifurcation = False


        # if np.mod(it, 10) == 0:
        #     alphatest = dolfin.Function(V_alpha)
        #     with dolfin.XDMFFile(os.path.join(outdir, "output_postproc.xdmf")) as f:
        #         f.read_checkpoint(alphatest, "alpha-{}".format(it), 0)
        #         print('DEBUG: read step ', it)

        time_data_pd.to_json(os.path.join(outdir, "time_data.json"))

    from post_processing import plot_global_data
    print(time_data_pd)
    print()
    print('Output in: '+outdir)


    # if size == 1:
    #     plt.figure()
    #     dolfin.plot(alpha)
    #     plt.savefig(os.path.join(outdir, "alpha.png"))
    #     plt.figure()
    #     dolfin.plot(u, mode="displacement")
    #     plt.savefig(os.path.join(outdir, "u.png"))
    #     plt.close('all')

    return time_data_pd
def traction_test(ell=0.05,
                  ell_e=.1,
                  degree=1,
                  n=3,
                  nu=0.,
                  load_min=0,
                  load_max=2,
                  loads=None,
                  nsteps=20,
                  Lx=1.,
                  Ly=0.1,
                  outdir="outdir",
                  postfix='',
                  savelag=1,
                  sigma_D0=1.,
                  periodic=False,
                  continuation=False,
                  checkstability=True,
                  configString='',
                  test=True):
    # constants
    # ell = ell
    Lx = Lx
    load_min = load_min
    load_max = load_max
    nsteps = nsteps
    outdir = outdir
    loads = loads

    savelag = 1
    nu = dolfin.Constant(nu)
    ell = dolfin.Constant(ell)
    ell_e = ell_e
    E = dolfin.Constant(1.0)
    K = E.values()[0] / ell_e**2.
    sigma_D0 = E
    n = n
    # h = ell.values()[0]/n
    h = max(ell.values()[0] / n, .005)
    cell_size = h
    continuation = continuation
    isPeriodic = periodic
    config = json.loads(configString) if configString != '' else ''

    cmd_parameters = {
        'material': {
            "ell": ell.values()[0],
            "ell_e": ell_e,
            "K": K,
            "E": E.values()[0],
            "nu": nu.values()[0],
            "sigma_D0": sigma_D0.values()[0]
        },
        'geometry': {
            'Lx': Lx,
            'Ly': Ly,
            'n': n,
        },
        'experiment': {
            'periodic': isPeriodic,
            'signature': ''
        },
        'stability': {
            'checkstability': checkstability,
            'continuation': continuation
        },
        'time_stepping': {
            'load_min': load_min,
            'load_max': load_max,
            'nsteps': nsteps,
            'outdir': outdir,
            'postfix': postfix,
            'savelag': savelag
        },
        'alt_min': {},
        "code": {}
    }

    # --------------------

    for par in parameters:
        parameters[par].update(cmd_parameters[par])

    if config:
        # import pdb; pdb.set_trace()
        for par in config:
            parameters[par].update(config[par])
    # else:

    # parameters['material']['ell_e'] =
    # import pdb; pdb.set_trace()
    Lx = parameters['geometry']['Lx']
    Ly = parameters['geometry']['Ly']
    ell = parameters['material']['ell']
    ell_e = parameters['material']['ell_e']

    BASE_DIR = os.path.dirname(os.path.realpath(__file__))
    fname = "film"
    print(BASE_DIR)
    os.path.isfile(fname)

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    print('Signature is: ' + signature)

    # if parameters['experiment']['test'] == True: outdir += '-{}'.format(cmd_parameters['time_stepping']['postfix'])
    # else:
    outdir += '-{}{}'.format(signature,
                             cmd_parameters['time_stepping']['postfix'])

    parameters['time_stepping']['outdir'] = outdir
    Path(outdir).mkdir(parents=True, exist_ok=True)
    print('Outdir is: ' + outdir)

    with open(os.path.join(outdir, 'rerun.sh'), 'w') as f:
        configuration = deepcopy(parameters)
        configuration['time_stepping'].pop('outdir')
        str(configuration).replace("\'True\'",
                                   "True").replace("\'False\'", "False")
        rerun_cmd = 'python3 {} --config="{}"'.format(
            os.path.basename(__file__), configuration)
        f.write(rerun_cmd)

    with open(os.path.join(outdir, 'parameters.pkl'), 'w') as f:
        json.dump(parameters, f)

    with open(os.path.join(outdir, 'signature.md5'), 'w') as f:
        f.write(signature)
    print(parameters)

    # ------------------
    geometry_parameters = parameters['geometry']

    geom_signature = hashlib.md5(
        str(geometry_parameters).encode('utf-8')).hexdigest()
    meshfile = "%s/meshes/circle-%s.xml" % (BASE_DIR, geom_signature)
    # cmd_parameters['experiment']['signature']=signature
    meshsize = parameters['material']['ell'] / parameters['geometry']['n']
    d = {
        'rad': parameters['geometry']['Lx'],
        'Ly': parameters['geometry']['Ly'],
        'meshsize': meshsize
    }

    if os.path.isfile(meshfile):
        print("Meshfile %s exists" % meshfile)
        mesh = dolfin.Mesh("meshes/circle-%s.xml" % (geom_signature))
    else:
        print("Creating meshfile: %s" % meshfile)
        print("DEBUG: parameters: %s" % parameters['geometry'])

        mesh_template = open('templates/circle_template.geo')

        src = Template(mesh_template.read())
        geofile = src.substitute(d)

        if MPI.rank(MPI.comm_world) == 0:
            with open("meshes/circle-%s" % geom_signature + ".geo", 'w') as f:
                f.write(geofile)

            cmd1 = 'gmsh meshes/circle-{}.geo -2 -o meshes/circle-{}.msh'.format(
                geom_signature, geom_signature)
            cmd2 = 'dolfin-convert -i gmsh meshes/circle-{}.msh meshes/circle-{}.xml'.format(
                geom_signature, geom_signature)

            print(
                'Unable to handle mesh generation at the moment, please generate the mesh and test again.'
            )
            print(cmd1)
            print(cmd2)
            sys.exit()
            print(check_output([cmd1], shell=True)
                  )  # run in shell mode in case you are not run in terminal
            Popen([cmd2], stdout=PIPE, shell=True).communicate()

        mesh = Mesh('meshes/circle-{}.xml'.format(geom_signature))
        mesh_xdmf = XDMFFile("meshes/circle-%s.xdmf" % (geom_signature))
        mesh_xdmf.write(mesh)

        # with pygmsh.geo.Geometry() as geom:
        #     circle = geom.add_circle(
        #         [0.0, 0.0, 0.0],
        #         1.0,
        #         mesh_size=0.1,
        #         num_sections=4,
        #         # If compound==False, the section borders have to be points of the
        #         # discretization. If using a compound circle, they don't; gmsh can
        #         # choose by itself where to point the circle points.
        #         compound=True,
        #     )
        #     geom.add_physical(circle.plane_surface, "disk")
        #     #
        #     mesh = geom.generate_mesh()

        # mesh.write("out.xdmf")
        # mesh.write("out.xml")

        # geom = mshr.Rectangle(dolfin.Point(-Lx/2., -Ly/2.), dolfin.Point(Lx/2., Ly/2.))
        # mesh = mshr.generate_mesh(geom, n * int(float(Lx / ell)))

    print(meshfile)

    mesh_xdmf = dolfin.XDMFFile("meshes/%s-%s.xdmf" % (fname, geom_signature))
    mesh_xdmf.write(mesh)
    if rank == 0:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        meshf << mesh

    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    u = dolfin.Function(V_u, name="Total displacement")
    alpha = dolfin.Function(V_alpha, name="Damage")

    bcs_alpha = []
    # Rectangle
    # bcs_u = [DirichletBC(V_u, Constant((0., 0)), '(near(x[0], %f) or near(x[0], %f))'%(-Lx/2., Lx/2.))]
    # Circle

    bcs_u = [DirichletBC(V_u, Constant((0., 0.)), 'on_boundary')]
    state = [u, alpha]

    Z = dolfin.FunctionSpace(
        mesh, dolfin.MixedElement([u.ufl_element(),
                                   alpha.ufl_element()]))
    z = dolfin.Function(Z)

    v, beta = dolfin.split(z)
    dx = dolfin.Measure("dx", metadata=form_compiler_parameters, domain=mesh)
    ds = dolfin.Measure("ds")

    # Files for output
    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
    file_con = dolfin.XDMFFile(os.path.join(outdir, "continuation.xdmf"))
    file_bif = dolfin.XDMFFile(
        os.path.join(outdir, "bifurcation_postproc.xdmf"))

    for f in [file_out, file_eig, file_con, file_bif]:
        f.parameters["functions_share_mesh"] = True
        f.parameters["flush_output"] = True

    # Problem

    foundation_density = 1. / 2. * 1. / ell_e**2. * dot(u, u)
    model = DamagePrestrainedElasticityModel(
        state,
        E,
        nu,
        ell,
        sigma_D0,
        user_functional=foundation_density,
        eps0t=Expression([['t', 0.], [0., 't']], t=0., degree=0))
    # import pdb; pdb.set_trace()
    model.dx = dx
    model.ds = ds
    energy = model.total_energy_density(u, alpha) * dx
    # Alternate minimization solver
    solver = solvers.AlternateMinimizationSolver(
        energy, [u, alpha], [bcs_u, bcs_alpha],
        parameters=parameters['alt_min'])

    rP = model.rP(u, alpha, v, beta) * dx + 1 / ell_e**2. * dot(v, v) * dx
    rN = model.rN(u, alpha, beta) * dx

    # stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z, parameters = parameters['stability'])
    stability = StabilitySolver(mesh,
                                energy, [u, alpha], [bcs_u, bcs_alpha],
                                z,
                                parameters=parameters['stability'],
                                rayleigh=[rP, rN])

    load_steps = np.linspace(load_min, load_max,
                             parameters['time_stepping']['nsteps'])
    if loads:
        load_steps = loads

    time_data = []

    linesearch = LineSearch(energy, [u, alpha])
    alpha_old = dolfin.Function(alpha.function_space())
    lmbda_min_prev = 0.000001
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    for it, load in enumerate(load_steps):
        model.eps0t.t = load
        alpha_old.assign(alpha)

        ColorPrint.print_warn('Solving load t = {:.2f}'.format(load))

        # First order stability conditions
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions
        (stable, negev) = stability.solve(solver.problem_alpha.lb)
        ColorPrint.print_pass(
            'Current state is{}stable'.format(' ' if stable else ' un'))

        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        print('lmbda min', lmbda_min_prev)
        print('mineig', mineig)
        Deltav = (mineig - lmbda_min_prev) if hasattr(stability, 'eigs') else 0

        if (mineig + Deltav) * (lmbda_min_prev +
                                dolfin.DOLFIN_EPS) < 0 and not bifurcated:
            bifurcated = True

            # save 3 bif modes
            print('About to bifurcate load ', load, 'step', it)
            bifurcation_loads.append(load)
            print('DEBUG: decide what to do')
            # save_current_bifurcation = True
            bifurc_i += 1

        lmbda_min_prev = mineig if hasattr(stability, 'mineig') else 0.

        if stable:
            solver.update()
        else:
            # Continuation
            iteration = 1
            while stable == False:
                # linesearch
                perturbation_v = stability.perturbation_v
                perturbation_beta = stability.perturbation_beta

                h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
                    [u, alpha, alpha_old], perturbation_v, perturbation_beta)

                if h_opt != 0:
                    save_current_bifurcation = True
                    alpha_bif.assign(alpha)
                    alpha_bif_old.assign(alpha_old)
                    # admissible
                    uval = u.vector()[:] + h_opt * perturbation_v.vector()[:]
                    aval = alpha.vector(
                    )[:] + h_opt * perturbation_beta.vector()[:]

                    u.vector()[:] = uval
                    alpha.vector()[:] = aval

                    u.vector().vec().ghostUpdate()
                    alpha.vector().vec().ghostUpdate()

                    # import pdb; pdb.set_trace()
                    (time_data_i, am_iter) = solver.solve()
                    (stable, negev) = stability.solve(alpha_old)
                    ColorPrint.print_pass(
                        '    Continuation iteration {}, current state is{}stable'
                        .format(iteration, ' ' if stable else ' un'))
                    iteration += 1

                else:
                    # warn
                    ColorPrint.print_warn(
                        'Found zero increment, we are stuck in the matrix')
                    ColorPrint.print_warn('Continuing load program')
                    break

            solver.update()
            # stable == True

        time_data_i["load"] = load
        time_data_i["stable"] = stable
        time_data_i["dissipated_energy"] = dolfin.assemble(
            model.damage_dissipation_density(alpha) * dx)
        time_data_i["foundation_energy"] = dolfin.assemble(
            1. / 2. * 1 / ell_e**2. * dot(u, u) * dx)
        time_data_i["membrane_energy"] = dolfin.assemble(
            model.elastic_energy_density(model.eps(u), alpha) * dx)
        time_data_i["elastic_energy"] = time_data_i[
            "membrane_energy"] + time_data_i["foundation_energy"]
        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev

        _sigma = model.stress(model.eps(u), alpha)
        e1 = dolfin.Constant([1, 0])
        _snn = dolfin.dot(dolfin.dot(_sigma, e1), e1)
        time_data_i["sigma"] = 1 / Ly * dolfin.assemble(_snn * model.ds(1))

        time_data_i["S(alpha)"] = dolfin.assemble(1. / (model.a(alpha)) *
                                                  model.dx)
        time_data_i["A(alpha)"] = dolfin.assemble((model.a(alpha)) * model.dx)
        time_data_i["avg_alpha"] = 1 / dolfin.assemble(
            dolfin.project(Constant(1.), V_alpha) *
            model.dx) * dolfin.assemble(alpha * model.dx)

        ColorPrint.print_pass(
            "Time step {:.4g}: it {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"], time_data_i["iterations"],
                time_data_i["alpha_error"]))

        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)

        if np.mod(it, savelag) == 0:
            with file_out as f:
                f.write(alpha, load)
                f.write(u, load)
                # with file_out as f:
                f.write_checkpoint(alpha,
                                   "alpha-{}".format(it),
                                   0,
                                   append=True)
                print('DEBUG: written step ', it)

        if save_current_bifurcation:
            # modes = np.where(stability.eigs < 0)[0]

            time_data_i['h_opt'] = h_opt
            time_data_i['max_h'] = hmax
            time_data_i['min_h'] = hmin

            with file_bif as file:
                # leneigs = len(modes)
                # maxmodes = min(3, leneigs)
                beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                print('DEBUG: irrev ', alpha.vector() - alpha_old.vector())
                file.write_checkpoint(beta0v, 'beta0', 0, append=True)
                file.write_checkpoint(alpha_bif_old,
                                      'alpha-old',
                                      0,
                                      append=True)
                file.write_checkpoint(alpha_bif, 'alpha-bif', 0, append=True)
                file.write_checkpoint(alpha, 'alpha', 0, append=True)

                np.save(os.path.join(outdir, 'energy_perturbations'),
                        energy_perturbations,
                        allow_pickle=True,
                        fix_imports=True)

            with file_eig as file:
                _v = dolfin.project(
                    dolfin.Constant(h_opt) * perturbation_v, V_u)
                _beta = dolfin.project(
                    dolfin.Constant(h_opt) * perturbation_beta, V_alpha)
                _v.rename('perturbation displacement',
                          'perturbation displacement')
                _beta.rename('perturbation damage', 'perturbation damage')
                # import pdb; pdb.set_trace()
                f.write(_v, load)
                f.write(_beta, load)
                file.write_checkpoint(_v, 'perturbation_v', 0, append=True)
                file.write_checkpoint(_beta,
                                      'perturbation_beta',
                                      0,
                                      append=True)

        time_data_pd.to_json(os.path.join(outdir, "time_data.json"))
        # user_postprocess_timestep(alpha, parameters, load, xresol = 100)

    plt.figure()
    dolfin.plot(alpha)
    plt.savefig(os.path.join(outdir, "alpha.png"))
    plt.figure()
    dolfin.plot(u, mode="displacement")
    plt.savefig(os.path.join(outdir, "u.png"))
    _nu = parameters['material']['nu']
    _E = parameters['material']['E']
    _w1 = parameters['material']['sigma_D0']**2. / parameters['material']['E']

    tc = np.sqrt(2 * _w1 / (_E * (1. - 2. * _nu) * (1. + _nu)))
    if parameters['stability']['checkstability'] == 'True':
        pp.plot_spectrum(parameters, outdir, time_data_pd.sort_values('load'),
                         tc)
    # plt.show()

    collect_timings(outdir, t0)

    print(time_data_pd)
    return time_data_pd
Exemplo n.º 4
0
def traction_1d(
    ell=0.1,
    degree=1,
    n=3,
    E=1.,
    load_min=0,
    load_max=2,
    loads=None,
    nsteps=20,
    Lx=1,
    outdir="outdir",
    postfix='',
    savelag=1,
    sigma_D0=1.,
    continuation=False,
    checkstability=True,
    configString='',
    breakifunstable=False,
):
    # constants
    ell = ell
    Lx = Lx
    load_min = load_min
    load_max = load_max
    nsteps = nsteps
    outdir = outdir
    loads = loads

    savelag = 1
    ell = dolfin.Constant(ell)
    E0 = dolfin.Constant(E)
    sigma_D0 = E0
    n = n
    continuation = continuation
    config = json.loads(configString) if configString != '' else ''

    cmd_parameters = {
        'material': {
            "ell": ell.values()[0],
            "E": E0.values()[0],
            "sigma_D0": sigma_D0.values()[0]
        },
        'geometry': {
            'Lx': Lx,
            'n': n,
        },
        'experiment': {
            'signature': '',
            'break-if-unstable': breakifunstable
        },
        'stability': {
            'checkstability': checkstability,
            'continuation': continuation
        },
        'time_stepping': {
            'load_min': load_min,
            'load_max': load_max,
            'nsteps': nsteps,
            'outdir': outdir,
            'postfix': postfix,
            'savelag': savelag
        },
        'alt_min': {},
        "code": {}
    }

    if config:
        for par in config:
            parameters[par].update(config[par])
    else:
        for par in parameters:
            parameters[par].update(cmd_parameters[par])
    print(parameters)

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir += '-{}{}'.format(signature,
                             cmd_parameters['time_stepping']['postfix'])
    # outdir += '-{}'.format(cmd_parameters['time_stepping']['postfix'])
    parameters['time_stepping']['outdir'] = outdir
    Path(outdir).mkdir(parents=True, exist_ok=True)
    print('Outdir is: ' + outdir)

    with open(os.path.join(outdir, 'rerun.sh'), 'w') as f:
        configuration = deepcopy(parameters)
        configuration['time_stepping'].pop('outdir')
        str(configuration).replace("\'True\'",
                                   "True").replace("\'False\'", "False")
        rerun_cmd = 'python3 {} --config="{}"'.format(
            os.path.basename(__file__), configuration)
        f.write(rerun_cmd)

    with open(os.path.join(outdir, 'parameters.pkl'), 'w') as f:
        json.dump(parameters, f)

    with open(os.path.join(outdir, 'signature.md5'), 'w') as f:
        f.write(signature)

    print('experiment = {}'.format(
        os.path.join('~/Documents/WIP/paper_stability_code', outdir)))
    mesh = dolfin.IntervalMesh(int(float(n * Lx / ell)), -Lx / 2., Lx / 2.)
    meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
    meshf << mesh

    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)

    mf = dolfin.MeshFunction("size_t", mesh, 1, 0)
    right.mark(mf, 1)
    left.mark(mf, 2)
    # bottom.mark(mf, 3)
    ds = dolfin.Measure("ds", subdomain_data=mf)
    dx = dolfin.Measure("dx", metadata=form_compiler_parameters, domain=mesh)

    # Function Spaces
    V_u = dolfin.FunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    u = dolfin.Function(V_u, name="Total displacement")
    alpha = dolfin.Function(V_alpha, name="Damage")
    state = [u, alpha]

    Z = dolfin.FunctionSpace(
        mesh, dolfin.MixedElement([u.ufl_element(),
                                   alpha.ufl_element()]))
    z = dolfin.Function(Z)

    v, beta = dolfin.split(z)

    # BCs (homogenous version needed for residual evaluation)
    ut = dolfin.Expression("t", t=0.0, degree=0)
    bcs_u = [
        dolfin.DirichletBC(V_u, dolfin.Constant(0), left),
        dolfin.DirichletBC(V_u, ut, right)
    ]

    bcs_alpha = []

    # bcs_alpha = [dolfin.DirichletBC(V_alpha, dolfin.Constant(1.), right)]

    # Files for output
    ColorPrint.print_warn('Outdir = {}'.format(outdir))
    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_out.parameters["functions_share_mesh"] = True
    file_out.parameters["flush_output"] = True
    file_con = dolfin.XDMFFile(os.path.join(outdir, "cont.xdmf"))
    file_con.parameters["functions_share_mesh"] = True
    file_con.parameters["flush_output"] = True
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "modes.xdmf"))
    file_eig.parameters["functions_share_mesh"] = True
    file_eig.parameters["flush_output"] = True

    # Problem definition
    # model = DamageElasticityModel1D(state, E0, ell, sigma_D0)
    #
    k_ell = 1e-8
    a = (1 - alpha)**2. + k_ell
    w_1 = parameters['material']['sigma_D0']**2 / parameters['material']['E']
    w = w_1 * alpha
    eps = u.dx(0)

    # sigma = parameters['material']['E']*eps
    # energy = 1./2.* parameters['material']['E']*a*eps**2. * dx + (w + w_1 * parameters['material']['ell'] ** 2. * alpha.dx(0)**2.)*dx

    # # Rayleigh Ratio
    # rP = (dolfin.sqrt(a)*sigma + dolfin.diff(a, alpha)/dolfin.sqrt(a)*sigma*beta)*(dolfin.sqrt(a)*v.dx(0) + dolfin.diff(a, alpha)/dolfin.sqrt(a)*eps*beta)*dx + \
    #                 2*w_1*parameters['material']['ell'] ** 2 * beta.dx(0)**2*dx

    # da = dolfin.diff(a, alpha)
    # dda = dolfin.diff(dolfin.diff(a, alpha), alpha)
    # ddw = dolfin.diff(dolfin.diff(w, alpha), alpha)

    # rN = -(1./2.*(dda - da**2./a)*sigma*eps +1./2.*ddw)*beta**2.*dx
    # import pdb; pdb.set_trace()

    # ------------------------------

    model = DamageElasticityModel1D(state, E0, ell, sigma_D0)
    model.dx = dx
    # energy = model.total_energy_density(u, alpha)*model.dx
    energy = 1. / 2. * parameters['material']['E'] * a * eps**2. * dx + (
        w + w_1 * parameters['material']['ell']**2. * alpha.dx(0)**2.) * dx
    rP = model.rP(u, alpha, v, beta) * model.dx
    rN = model.rN(u, alpha, beta) * model.dx

    # Alternate minimisation solver
    # import pdb; pdb.set_trace()
    solver = solvers.AlternateMinimizationSolver(
        energy, [u, alpha], [bcs_u, bcs_alpha],
        parameters=parameters['alt_min'])

    stability = StabilitySolver(mesh,
                                energy, [u, alpha], [bcs_u, bcs_alpha],
                                z,
                                rayleigh=[rP, rN],
                                parameters=parameters['stability'])
    # stability = StabilitySolver(mesh, energy, [u, alpha], [bcs_u, bcs_alpha], z, parameters = parameters['stability'])

    # Time iterations
    load_steps = np.linspace(load_min, load_max,
                             parameters['time_stepping']['nsteps'])
    # load_steps = np.logspace(np.log10(load_min), np.log10(load_max), parameters['time_stepping']['nsteps'])

    if loads:
        load_steps = loads

    stability.parameters['checkstability'] = True

    time_data = []

    linesearch = LineSearch(energy, [u, alpha])
    alpha_old = dolfin.Function(alpha.function_space())
    lmbda_min_prev = 0.000001
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)

    lmbda_min_prev = 0.000001
    bifurcated = False

    bifurcation_loads = []
    time_data_pd = []
    for it, load in enumerate(load_steps):
        # import pdb; pdb.set_trace()
        ut.t = load
        alpha_old.assign(alpha)

        ColorPrint.print_warn('Solving load t = {:.2f}'.format(load))

        # First order stability conditions
        (time_data_i, am_iter) = solver.solve()
        # import pdb; pdb.set_trace()

        # Second order stability conditions
        (stable, negev) = stability.solve(solver.problem_alpha.lb)
        ColorPrint.print_pass(
            'Current state is{}stable'.format(' ' if stable else ' un'))

        solver.update()
        #
        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        print('lmbda min', lmbda_min_prev)
        print('mineig', mineig)
        Deltav = (mineig - lmbda_min_prev) if hasattr(stability, 'eigs') else 0

        if (mineig + Deltav) * (lmbda_min_prev +
                                dolfin.DOLFIN_EPS) < 0 and not bifurcated:
            bifurcated = True

            # save 3 bif modes
            print('About to bifurcate load ', load, 'step', it)
            bifurcation_loads.append(load)
            modes = np.where(stability.eigs < 0)[0]

            with dolfin.XDMFFile(os.path.join(outdir,
                                              "postproc.xdmf")) as file:
                leneigs = len(modes)
                maxmodes = min(3, leneigs)
                for n in range(maxmodes):
                    mode = dolfin.project(stability.linsearch[n]['beta_n'],
                                          V_alpha)
                    modename = 'beta-%d' % n
                    print(modename)
                    file.write_checkpoint(mode, modename, 0, append=True)

            bifurc_i += 1

        lmbda_min_prev = mineig if hasattr(stability, 'mineig') else 0.

        # stable == True

        time_data_i["load"] = load
        time_data_i["stable"] = stable

        time_data_i["elastic_energy"] = dolfin.assemble(
            1. / 2. * parameters['material']['E'] * a * eps**2. * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * parameters['material']['ell']**2. * alpha.dx(0)**2.) *
            dx)

        time_data_i["elastic_energy"] = dolfin.assemble(
            model.elastic_energy_density(u.dx(0), alpha) * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            model.damage_dissipation_density(alpha) * dx)

        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        # import pdb; pdb.set_trace()

        # time_data_i["S(alpha)"] = dolfin.assemble(1./(a)*dx)
        # time_data_i["a(alpha)"] = dolfin.assemble(a*dx)
        # time_data_i["avg_alpha"] = dolfin.assemble(alpha*dx)

        ColorPrint.print_pass(
            "Time step {:.4g}: it {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"], time_data_i["iterations"],
                time_data_i["alpha_error"]))

        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)

        if np.mod(it, savelag) == 0:
            with file_out as f:
                f.write(alpha, load)
                f.write(u, load)
            with dolfin.XDMFFile(os.path.join(outdir,
                                              "output_postproc.xdmf")) as f:
                f.write_checkpoint(alpha,
                                   "alpha-{}".format(it),
                                   0,
                                   append=True)
                print('DEBUG: written step ', it)

        time_data_pd.to_json(os.path.join(outdir, "time_data.json"))

        # if size == 1:
        #     plt.figure()
        #     dolfin.plot(alpha, marker='o')
        #     plt.savefig(os.path.join(outdir, "alpha-{}.png".format(it)))
        #     plt.figure()
        #     dolfin.plot(u, marker='o')
        #     plt.savefig(os.path.join(outdir, "u-{}.png".format(it)))
        #     plt.clf()

        if not stable and parameters['experiment']['break-if-unstable']:
            print('Unstable state, breaking',
                  parameters['experiment']['break-if-unstable'])
            break

    # print(time_data_pd)
    print()
    # print(time_data_pd['stable'])
    print('Output in: ' + outdir)

    # # solve optimal profile
    #   # Alternate minimisation solver
    # beta = dolfin.TestFunction(V_alpha)
    # dalpha = dolfin.TrialFunction(V_alpha)

    # F = (ell*alpha.dx(0)*alpha.dx(0) + w/ell*alpha)*dx
    # dF = dolfin.derivative(F,alpha,beta); ddF = dolfin.derivative(dF,alpha,dalpha)
    # # bcs_u = [dolfin.DirichletBC(V_u, dolfin.Constant(0.), 'on_boundary')]
    alpha = dolfin.Function(V_alpha)
    u = dolfin.Function(V_u)
    bcs_alpha = [
        dolfin.DirichletBC(V_alpha, dolfin.Constant(1.), right),
        dolfin.DirichletBC(V_alpha, dolfin.Constant(0.), left)
    ]
    solver = solvers.AlternateMinimizationSolver(
        energy, [u, alpha], [[], bcs_alpha], parameters=parameters['alt_min'])
    solver.solve()
    print('DEBUG: h1 norm alpha profile {}'.format(dolfin.norm(alpha, 'h1')))
    # ub = dolfin.interpolate(dolfin.Constant(1.), V_alpha); lb = dolfin.interpolate(dolfin.Constant(0.), V_alpha)
    # profile = dolfin.NonlinearVariationalProblem(dF, alpha, bcs_alpha, J = ddF)
    # profile.set_bounds(lb, ub)
    # solver_nl = dolfin.NonlinearVariationalSolver(profile)
    # snes_solver_parameters_bounds = {"nonlinear_solver": "snes",
    #                       "snes_solver": {"linear_solver": "cg",
    #                                       "maximum_iterations": 100,
    #                                       "report": True,
    #                                       "line_search": "basic",
    #                                       "method":"vinewtonrsls",
    #                                       "absolute_tolerance":1e-6,
    #                                       "relative_tolerance":1e-6,
    #                                       "solution_tolerance":1e-6}}
    # solver_nl.parameters.update(snes_solver_parameters_bounds)
    # solver_nl.solve()

    xs = np.linspace(-Lx / 2., Lx / 2., 100)
    profile = np.array([alpha(x) for x in xs])
    plt.figure()
    plt.plot(xs, profile, marker='o')
    # plt.plot(xs, np.array([u(x) for x in xs]))
    plt.savefig(os.path.join(outdir, 'profile.pdf'))
    # import pdb; pdb.set_trace()

    return time_data_pd, outdir
def traction_test(
    ell=0.1,
    degree=1,
    n=3,
    nu=0.0,
    load_min=0,
    load_max=2,
    loads=None,
    nsteps=20,
    Lx=1,
    Ly=0.1,
    outdir="outdir",
    savelag=1,
):
    # constants
    ell = ell
    Lx = Lx
    Ly = Ly
    load_min = load_min
    load_max = load_max
    nsteps = nsteps
    loads=loads

    savelag = 1
    nu = dolfin.Constant(nu)
    ell = dolfin.Constant(ell)
    E0 = dolfin.Constant(1.0)
    sigma_D0 = E0
    n = n


    params =  { 
    'material': {
        "ell":   ell.values()[0],
        "E": E0.values()[0],
        "nu": nu.values()[0],
        "sigma_D0": sigma_D0.values()[0]},
    'geometry': {
        'Lx': Lx,
        'Ly': Ly,
        'n': n,
        },
    'load':{
        'min': load_min,
        'max': load_max,
        'nsteps':  nsteps
    } }

    print(params)
    geom = mshr.Rectangle(dolfin.Point(-Lx/2., -Ly/2.), dolfin.Point(Lx/2., Ly/2.))

    nel = max(int(n * float(Lx / ell)), int(Ly/3.))
    mesh = mshr.generate_mesh(geom, nel)

    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    right_bottom_pt = dolfin.CompiledSubDomain("near(x[1], Lx/2.) && near(x[0],-Ly/2.)", Lx=Lx, Ly=Ly)

    mf = dolfin.MeshFunction("size_t", mesh, 1, 0)
    right.mark(mf, 1)
    left.mark(mf, 2)
    ds = dolfin.Measure("ds", subdomain_data=mf)
    dx = dolfin.Measure("dx", metadata=form_compiler_parameters, domain=mesh)

    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    u = dolfin.Function(V_u, name="Total displacement")
    alpha = dolfin.Function(V_alpha, name="Damage")
    state = [u, alpha]

    Z = dolfin.FunctionSpace(mesh, dolfin.MixedElement([u.ufl_element(),alpha.ufl_element()]))
    z = dolfin.Function(Z)

    v, beta = dolfin.split(z)

    ut = dolfin.Expression("t", t=0.0, degree=0)
    bcs_u = [dolfin.DirichletBC(V_u.sub(0), dolfin.Constant(0), left),
             dolfin.DirichletBC(V_u.sub(0), ut, right),
             dolfin.DirichletBC(V_u, (0, 0), right_bottom_pt, method="pointwise")]

    bcs_alpha = []

    # Problem definition
    model = DamageElasticityModel(state, E0, nu, ell, sigma_D0)
    energy = model.total_energy_density(u, alpha)*dx

    # Alternate minimization solver
    solver = solvers.AlternateMinimizationSolver(
        energy, [u, alpha], [bcs_u, bcs_alpha], parameters = alt_min_parameters)

    rP = model.rP(u, alpha, v, beta)*dx
    rN = model.rN(u, alpha, beta)*dx

    stability = StabilitySolver(mesh, energy,
        [u, alpha], [bcs_u, bcs_alpha], z, rayleigh=[rP, rN], parameters = stability_parameters)

    # Time iterations
    time_data = []
    load_steps = np.linspace(load_min, load_max, nsteps)
    alpha_old = dolfin.Function(V_alpha)

    for it, load in enumerate(load_steps):
        stable = None; negev = 0; mineig = np.inf; iteration = 0
        ut.t = load
        ColorPrint.print_pass('load: {:4f} step {:d} ell {:f}'.format(load, it, ell.values()[0]))
        alpha_old.assign(alpha)
        time_data_i, am_iter = solver.solve()
        solver.update()
        (stable, negev) = stability.solve(alpha_old)

        time_data_i["load"] = load
        time_data_i["stable"] = stable
        time_data_i["# neg ev"] = negev
        time_data_i["elastic_energy"] = dolfin.assemble(
            model.elastic_energy_density(model.eps(u), alpha)*dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            model.damage_dissipation_density(alpha)*dx)
        time_data_i["eigs"] = stability.eigs if hasattr(stability, 'eigs') else np.inf
        time_data_i["max alpha"] = np.max(alpha.vector()[:])
        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)

        if stable == False:
            break

    return time_data_pd