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': {
            '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
예제 #3
0
def numerical_test(
    user_parameters
):
    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurcation_loads = []

    comm = MPI.comm_world

    default_parameters = getDefaultParameters()
    default_parameters.update(user_parameters)

    parameters = default_parameters
    parameters['code']['script'] = __file__

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir = '../output/traction/{}-{}CPU'.format(signature, size)
    Path(outdir).mkdir(parents=True, exist_ok=True)

    log(LogLevel.INFO, 'Outdir is: '+outdir)
    BASE_DIR = os.path.dirname(os.path.realpath(__file__))
    print(parameters['geometry'])
    d={'Lx': parameters['geometry']['Lx'],'Ly': parameters['geometry']['Ly'],
        'h': parameters['material']['ell']/parameters['geometry']['n']}

    geom_signature = hashlib.md5(str(d).encode('utf-8')).hexdigest()

    Lx = parameters['geometry']['Lx']
    Ly = parameters['geometry']['Ly']
    n = parameters['geometry']['n']
    ell = parameters['material']['ell']
    fname = os.path.join('../meshes', 'strip-{}'.format(geom_signature))

    resolution = max(parameters['geometry']['n'] * Lx / ell, 5/(Ly*10))
    resolution = 3

    geom = mshr.Rectangle(dolfin.Point(-Lx/2., -Ly/2.), dolfin.Point(Lx/2., Ly/2.))
    mesh = mshr.generate_mesh(geom, resolution)

    log(LogLevel.INFO, 'Number of dofs: {}'.format(mesh.num_vertices()*(1+parameters['general']['dim'])))
    if size == 1:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        plot(mesh)
        plt.savefig(os.path.join(outdir, "mesh.pdf"), bbox_inches='tight')

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)

    Lx = parameters['geometry']['Lx']
    ell =  parameters['material']['ell']
    savelag = 1

    # Function Spaces
    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    L2 = dolfin.FunctionSpace(mesh, "DG", 0)
    u = dolfin.Function(V_u, name="Total displacement")
    u.rename('u', 'u')
    alpha = Function(V_alpha)
    alpha_old = dolfin.Function(alpha.function_space())
    alpha.rename('alpha', 'alpha')
    dalpha = TrialFunction(V_alpha)
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)


    state = {'u': u, 'alpha': alpha}
    Z = dolfin.FunctionSpace(mesh,
            dolfin.MixedElement([u.ufl_element(),alpha.ufl_element()]))
    z = dolfin.Function(Z)
    v, beta = dolfin.split(z)
    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)
    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 = []

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}

    ds = dolfin.Measure("ds", subdomain_data=mf)
    dx = dolfin.Measure("dx", metadata=parameters['compiler'], domain=mesh)

    ell = parameters['material']['ell']

    # -----------------------
    # Problem definition
    k_res = parameters['material']['k_res']
    a = (1 - alpha) ** 2. + k_res
    w_1 = parameters['material']['sigma_D0'] ** 2 / parameters['material']['E']
    w = w_1 * alpha
    eps = sym(grad(u))
    eps0t=Expression([['t', 0.],[0.,'t']], t=0., degree=0)
    lmbda0 = parameters['material']['E'] * parameters['material']['nu'] /(1. - parameters['material']['nu'])**2.
    mu0 = parameters['material']['E']/ 2. / (1.0 + parameters['material']['nu'])
    nu = parameters['material']['nu']
    sigma0 = lmbda0 * tr(eps)*dolfin.Identity(parameters['general']['dim']) + 2*mu0*eps
    e1 = Constant((1., 0))
    _sigma = ((1 - alpha) ** 2. + k_res)*sigma0
    _snn = dolfin.dot(dolfin.dot(_sigma, e1), e1)

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

    ell = parameters['material']['ell']
    E = parameters['material']['E']

    def elastic_energy(u,alpha, E=E, nu=nu, eps0t=eps0t, k_res=k_res):
        a = (1 - alpha) ** 2. + k_res
        eps = sym(grad(u))
        Wt = a*E*nu/(2*(1-nu**2.)) * tr(eps)**2.                                \
            + a*E/(2.*(1+nu))*(inner(eps, eps))
        return Wt * dx 

    def dissipated_energy(alpha,w_1=w_1,ell=ell):
        return w_1 *( alpha + ell** 2.*inner(grad(alpha), grad(alpha)))*dx

    def total_energy(u, alpha, k_res=k_res, w_1=w_1, 
                            E=E, 
                            nu=nu, 
                            ell=ell,
                            eps0t=eps0t):
        elastic_energy_ = elastic_energy(u,alpha, E=E, nu=nu, eps0t=eps0t, k_res=k_res)
        dissipated_energy_ = dissipated_energy(alpha,w_1=w_1,ell=ell)
        return elastic_energy_ + dissipated_energy_

    def energy_1d(h, perturbation_v=Function(u.function_space()), perturbation_beta=Function(alpha.function_space())):
        return assemble(total_energy(u + float(h) * perturbation_v,
                                alpha + float(h) * perturbation_beta))

    energy = total_energy(u,alpha)

    def create_output(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_postproc = dolfin.XDMFFile(os.path.join(outdir, "postprocess.xdmf"))
        file_postproc.parameters["functions_share_mesh"] = True
        file_postproc.parameters["flush_output"] = True
        file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
        file_eig.parameters["functions_share_mesh"] = True
        file_eig.parameters["flush_output"] = True
        file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation.xdmf"))
        file_bif.parameters["functions_share_mesh"] = True
        file_bif.parameters["flush_output"] = True
        file_bif_postproc = dolfin.XDMFFile(os.path.join(outdir, "bifurcation_postproc.xdmf"))
        file_bif_postproc.parameters["functions_share_mesh"] = True
        file_bif_postproc.parameters["flush_output"] = True
        file_ealpha = dolfin.XDMFFile(os.path.join(outdir, "elapha.xdmf"))
        file_ealpha.parameters["functions_share_mesh"] = True
        file_ealpha.parameters["flush_output"] = True

        files = {'output': file_out, 
                 'postproc': file_postproc,
                 'eigen': file_eig,
                 'bifurcation': file_bif,
                 'ealpha': file_ealpha}

        return files

    files = create_output(outdir)

    solver = EquilibriumAM(energy, state, bcs, parameters=parameters)
    stability = StabilitySolver(energy, state, bcs, parameters = parameters)
    linesearch = LineSearch(energy, state)

    load_steps = np.linspace(parameters['loading']['load_min'],
        parameters['loading']['load_max'],
        parameters['loading']['n_steps'])

    tc = (parameters['material']['sigma_D0']/parameters['material']['E'])**(.5)

    _eps = 1e-3
    load_steps = [0., tc-_eps, tc+_eps]

    time_data = []
    time_data_pd = []
    bifurcation_loads = []
    save_current_bifurcation = False

    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)

    bifurcation_loads = []

    save_bifurcation = 1

    log(LogLevel.INFO, '{}'.format(parameters))
    for step, load in enumerate(load_steps):
        plt.clf()
        mineigs = []
        exhaust_modes = []

        log(LogLevel.CRITICAL, '====================== STEPPING ==========================')
        log(LogLevel.CRITICAL, 'Solving load t = {:.2f}'.format(load))
        alpha_old.assign(alpha)
        ut.t = load
        (time_data_i, am_iter) = solver.solve()

        # Second order stability
        (stable, negev) = stability.solve(solver.damage.problem.lb)
        log(LogLevel.CRITICAL, 'Current state is{}stable'.format(' ' if stable else ' un'))

        if stable:
            solver.update()
        else:
            log(LogLevel.INFO, 'About to bifurcate load {:.3f} step {}'.format(load, step))

            iteration = 1
            mineigs.append(stability.mineig)

            while stable == False:
                log(LogLevel.INFO, 'Continuation iteration {}'.format(iteration))
                iteration += 1

                # plotstep()

                cont_data_pre = compile_continuation_data(state, energy)
                opt_mode = 0

                perturbation_v    = stability.perturbations_v[opt_mode]
                perturbation_beta = stability.perturbations_beta[opt_mode]

                (hmin, hmax) = linesearch.admissible_interval(alpha, alpha_old, perturbation_beta)

                hs = np.linspace(hmin, hmax, 20)
                energy_vals = np.array([energy_1d(h, perturbation_v, perturbation_beta) for h in hs])

                h_opt = hs[np.argmin(energy_vals)]

                log(LogLevel.INFO, 'Computed h_opt {}'.format(h_opt))

                perturbation = {'v': stability.perturbations_v[opt_mode], 
                                'beta': stability.perturbations_beta[opt_mode], 
                                'h': h_opt}

                perturbState(state, perturbation)

                (time_data_i, am_iter) = solver.solve(outdir)


                (stable, negev) = stability.solve(solver.damage.problem.lb)
                mineigs.append(stability.mineig)

                log(LogLevel.INFO, 'Continuation iteration {}, current state is{}stable'.format(iteration, ' ' if stable else ' un'))

                cont_data_post = compile_continuation_data(state, energy)

                # continuation criterion
                if abs(np.diff(mineigs)[-1]) > parameters['stability']['cont_rtol']:
                    log(LogLevel.INFO, 'Continuing perturbations')
                else:
                    log(LogLevel.CRITICAL, 'We are stuck in the matrix')
                    log(LogLevel.WARNING, 'Continuing load program')
                    break

            solver.update()

            if save_current_bifurcation:
                plotPerturbationData()
                savePerturbationData()
                save_current_bifurcation = False

        def compileTimeData(time_data_i, load):

            time_data_i["load"] = load
            time_data_i["alpha_max"] = max(alpha.vector()[:])
            time_data_i["elastic_energy"] = dolfin.assemble(elastic_energy(
                u,alpha, E=E, nu=nu, eps0t=eps0t, k_res=k_res))
            time_data_i["dissipated_energy"] = dolfin.assemble(
                (w + w_1 * parameters['material']['ell'] ** 2. * inner(grad(alpha), grad(alpha)))*dx)
            time_data_i["stable"] = stability.stable
            time_data_i["# neg ev"] = stability.negev
            time_data_i["eigs"] = stability.eigs if hasattr(stability, 'eigs') else np.inf
            time_data_i["sigma"] = 1/Ly * dolfin.assemble(_snn*ds(1))

            log(LogLevel.INFO,
                "Load/time step {:.4g}: converged in iterations: {:3d}, err_alpha={:.4e}".format(
                    time_data_i["load"],
                    time_data_i["iterations"][0],
                    time_data_i["alpha_error"][0]))

            return time_data_i

        time_data.append(compileTimeData(time_data_i, load))
        time_data_pd = pd.DataFrame(time_data)

        def outputData():
            np.save(os.path.join(outdir, 'bifurcation_loads'), bifurcation_loads, allow_pickle=True, fix_imports=True)

            with files['output'] as file:
                file.write(alpha, load)
                file.write(u, load)

            with files['postproc'] as file:
                file.write_checkpoint(alpha, "alpha-{}".format(step), step, append = True)
                file.write_checkpoint(u, "u-{}".format(step), step, append = True)
                log(LogLevel.INFO, 'Written postprocessing step {}'.format(step))

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

        outputData()
    return time_data_pd, outdir
예제 #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 numerical_test(
    user_parameters,
    ell=0.05,
    nu=0.,
):
    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    bifurcation_loads = []

    # Create mesh and define function space
    geometry_parameters = {'Lx': 1., 'Ly': .1, 'n': 5}

    # Define Dirichlet boundaries
    comm = MPI.comm_world
    outdir = '../test/output/test_film_firstorder'
    Path(outdir).mkdir(parents=True, exist_ok=True)

    with open('../parameters/form_compiler.yml') as f:
        form_compiler_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/solvers_default.yml') as f:
        solver_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/film.yaml') as f:
        material_parameters = yaml.load(f, Loader=yaml.FullLoader)['material']

    with open('../parameters/loading.yaml') as f:
        loading_parameters = yaml.load(f, Loader=yaml.FullLoader)['loading']

    with open('../parameters/stability.yaml') as f:
        stability_parameters = yaml.load(f, Loader=yaml.FullLoader)['stability']

    Path(outdir).mkdir(parents=True, exist_ok=True)

    log(LogLevel.INFO, 'INFO: Outdir is: '+outdir)

    default_parameters = {
        'code': {**code_parameters},
        'compiler': {**form_compiler_parameters},
        'geometry': {**geometry_parameters},
        'loading': {**loading_parameters},
        'material': {**material_parameters},
        'solver':{**solver_parameters},
        'stability': {**stability_parameters},
        }

    default_parameters.update(user_parameters)
    # FIXME: Not nice
    parameters = default_parameters

    Ly = parameters['geometry']['Ly']
    Lx = parameters['geometry']['Lx']
    geom = mshr.Rectangle(dolfin.Point(-Lx/2., -Ly/2.), dolfin.Point(Lx/2., Ly/2.))
    import pdb; pdb.set_trace()
    # resolution = max(geometry_parameters['n'] * Lx / ell, 1/(Ly*10))
    resolution = max(geometry_parameters['n'] * Lx / ell, 5/(Ly*10))
    resolution = 150
    mesh = mshr.generate_mesh(geom,  resolution)
    if size == 1:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        meshf << mesh
        plot(mesh)
        plt.savefig(os.path.join(outdir, "mesh.pdf"), bbox_inches='tight')

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)


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

    # import pdb; pdb.set_trace()


    savelag = 1
    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    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)

    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")
    u.rename('u', 'u')
    alpha = Function(V_alpha)
    alpha_old = dolfin.Function(alpha.function_space())
    alpha.rename('alpha', 'alpha')
    dalpha = TrialFunction(V_alpha)
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)


    state = {'u': u, 'alpha': 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, dolfin.Constant((0,0)), left),
             # dolfin.DirichletBC(V_u, dolfin.Constant((0,0)), right),
             # dolfin.DirichletBC(V_u, (0, 0), left_bottom_pt, method="pointwise")
             # ]
    # bcs_u = [dolfin.DirichletBC(V_u, dolfin.Constant((0,0)), 'on_boundary')]
    bcs_u = [dolfin.DirichletBC(V_u.sub(0), dolfin.Constant(0), left),
             dolfin.DirichletBC(V_u.sub(0), dolfin.Constant(0), right),
             ]
    # bcs_alpha_l = DirichletBC(V_alpha,  Constant(0.0), left)
    # bcs_alpha_r = DirichletBC(V_alpha, Constant(0.0), right)
    # bcs_alpha =[bcs_alpha_l, bcs_alpha_r]
    bcs_alpha = []

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}

    # import pdb; pdb.set_trace()

    ell = parameters['material']['ell']

    # Problem definition
    # Problem definition
    k_res = parameters['material']['k_res']
    a = (1 - alpha) ** 2. + k_res
    w_1 = parameters['material']['sigma_D0'] ** 2 / parameters['material']['E']
    w = w_1 * alpha
    eps = sym(grad(u))
    eps0t=Expression([['t', 0.],[0.,'t']], t=0., degree=0)
    lmbda0 = parameters['material']['E'] * parameters['material']['nu'] /(1. - parameters['material']['nu'])**2.
    mu0 = parameters['material']['E']/ 2. / (1.0 + parameters['material']['nu'])
    nu = parameters['material']['nu']
    Wt = a*parameters['material']['E']*nu/(2*(1-nu**2.)) * tr(eps-eps0t)**2.                                \
        + a*parameters['material']['E']*(inner(eps-eps0t, eps-eps0t)/(2.*(1+nu)))                           \
    + 1./2.*1./parameters['material']['ell_e']**2.*dot(u, u)

    energy = Wt * dx + w_1 *( alpha + parameters['material']['ell']** 2.*inner(grad(alpha), grad(alpha)))*dx

    # import pdb; pdb.set_trace()

    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_out.parameters["functions_share_mesh"] = True
    file_out.parameters["flush_output"] = True
    file_postproc = dolfin.XDMFFile(os.path.join(outdir, "postprocess.xdmf"))
    file_postproc.parameters["functions_share_mesh"] = True
    file_postproc.parameters["flush_output"] = True
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
    file_eig.parameters["functions_share_mesh"] = True
    file_eig.parameters["flush_output"] = True
    file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation.xdmf"))
    file_bif.parameters["functions_share_mesh"] = True
    file_bif.parameters["flush_output"] = True
    file_bif_postproc = dolfin.XDMFFile(os.path.join(outdir, "bifurcation_postproc.xdmf"))
    file_bif_postproc.parameters["functions_share_mesh"] = True
    file_bif_postproc.parameters["flush_output"] = True


    solver = EquilibriumSolver(energy, state, bcs, parameters=parameters['solver'])
    stability = StabilitySolver(energy, state, bcs, parameters = parameters['stability'])
    # stability = StabilitySolver(energy, state, bcs, parameters = parameters['stability'], rayleigh= [rP, rN])
    linesearch = LineSearch(energy, state)

    load_steps = np.linspace(parameters['loading']['load_min'],
        parameters['loading']['load_max'],
        parameters['loading']['n_steps'])

    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    bifurcation_loads = []
    perturb = False

    for step, load in enumerate(load_steps):
        log(LogLevel.CRITICAL, '====================== STEPPING ==========================')
        log(LogLevel.CRITICAL, 'CRITICAL: Solving load t = {:.2f}'.format(load))
        alpha_old.assign(alpha)
        eps0t.t = load
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions

        (stable, negev) = stability.solve(solver.damage.problem.lb)
        log(LogLevel.CRITICAL, 'Current state is{}stable'.format(' ' if stable else ' un'))


        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        log(LogLevel.INFO, 'INFO: lmbda min {}'.format(lmbda_min_prev))
        log(LogLevel.INFO, 'INFO: mineig {}'.format(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
            log(LogLevel.INFO, 'INFO: About to bifurcate load {} step {}'.format(load, step))
            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.

        # we postpone the update after the stability check
        # solver.update()
        log(LogLevel.INFO,'    Current state is{}stable'.format(' ' if stable else ' un'))

        if not stable:
            save_current_bifurcation = True
            perturbation_v    = stability.perturbation_v
            perturbation_beta = stability.perturbation_beta
            h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
                {'u':u, 'alpha':alpha, 'alpha_old': alpha_old},
                perturbation_v, perturbation_beta)

        # else:
        #     # Continuation
        #     iteration = 1
        #     while stable == False:
        #         # linesearch
        #         save_current_bifurcation = True

        #         cont_data_pre = compile_continuation_data(state, energy)
        #         perturbation_v    = stability.perturbation_v
        #         perturbation_beta = stability.perturbation_beta

        #         # import pdb; pdb.set_trace()

        #         h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
        #             {'u':u, 'alpha':alpha, 'alpha_old': alpha_old},
        #             perturbation_v, perturbation_beta)

        #         stable = True

        #         if h_opt != 0:
        #             log(LogLevel.INFO, '    Bifurcarting')
        #             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(solver.damage.problem.lb)
        #             log(LogLevel.INFO, '    Continuation iteration {}, current state is{}stable'.format(iteration, ' ' if stable else ' un'))
        #             iteration += 1
        #             cont_data_post = compile_continuation_data(state, energy)

        #             # # import pdb; pdb.set_trace()

        #             criterion = (cont_data_post['energy']-cont_data_pre['energy'])/cont_data_pre['energy'] < parameters['stability']['cont_rtol']
        #             log(LogLevel.INFO, 'INFO: Continuation criterion {}'.format(criterion))
        #         else:
        #             # warn
        #             log(LogLevel.WARNING, 'Found zero increment, we are stuck in the matrix')
        #             log(LogLevel.WARNING, 'Continuing load program')
        #             break

        solver.update()

            # 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_postproc as file:
                # leneigs = len(modes)
                # maxmodes = min(3, leneigs)
                beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                log(LogLevel.DEBUG, 'DEBUG: irrev {}'.format(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')
                file.write(_v, load)
                file.write(_beta, load)

        time_data_i["load"] = load
        time_data_i["alpha_max"] = max(alpha.vector()[:])
        time_data_i["elastic_energy"] = dolfin.assemble(
            a * Wt * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * material_parameters['ell'] ** 2. * inner(grad(alpha), grad(alpha)))*dx)
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        time_data_i["eigs"] = stability.eigs if hasattr(stability, 'eigs') else np.inf

        # eps_ = variable(eps)
        # import pdb; pdb.set_trace()

        # sigma = derivative( 1./2.* lmbda0 * tr(eps-eps0t)**2. + mu0 * inner(eps-eps0t, eps-eps0t), eps, eps_)
        # snn = dolfin.dot(dolfin.dot(sigma, e1), e1)
        # time_data_i["sigma"] = 1/parameters['geometry']['Ly'] * dolfin.assemble(snn*ds(1))

        log(LogLevel.INFO,
            "Load/time step {:.4g}: converged in iterations: {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"],
                time_data_i["iterations"][0],
                time_data_i["alpha_error"][0]))

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

        with file_out as file:
            file.write(alpha, load)
            file.write(u, load)

        with file_postproc as file:
            file.write_checkpoint(alpha, "alpha-{}".format(step), step, append = True)
            file.write_checkpoint(u, "u-{}".format(step), step, append = True)
            log(LogLevel.INFO, 'INFO: written postprocessing step {}'.format(step))


        spacetime.append(get_trace(alpha))


    time_data_pd.to_json(os.path.join(outdir, "time_data.json"))
    _spacetime = pd.DataFrame(spacetime)
    spacetime = _spacetime.fillna(0)
    mat = np.matrix(spacetime)
    plt.imshow(mat, cmap = 'Greys', vmin = 0., vmax = 1., aspect=.1)
    plt.colorbar()

    def format_space(x, pos, xresol = 100):
        return '$%1.1f$'%((-x+xresol/2)/xresol)

    def format_time(t, pos, xresol = 100):
        return '$%1.1f$'%((t-parameters['loading']['load_min'])/parameters['loading']['n_steps']*parameters['loading']['load_max'])

    from matplotlib.ticker import FuncFormatter, MaxNLocator

    ax = plt.gca()

    ax.yaxis.set_major_formatter(FuncFormatter(format_space))
    ax.xaxis.set_major_formatter(FuncFormatter(format_time))

    plt.xlabel('$x$')
    plt.ylabel('$t$')
    plt.savefig(os.path.join(outdir, "spacetime.pdf".format(load)), bbox_inches="tight")
    plt.clf()
    spacetime.to_json(os.path.join(outdir + "/spacetime.json"))

    from matplotlib.ticker import FuncFormatter, MaxNLocator
    plot(alpha)
    plt.savefig(os.path.join(outdir, 'alpha.pdf'))
    log(LogLevel.INFO, "Saved figure: {}".format(os.path.join(outdir, 'alpha.pdf')))

    # import pdb; pdb.set_trace()

    xs = np.linspace(-Lx/2., Lx/2., 300)
    alpha.set_allow_extrapolation(True)
    u.set_allow_extrapolation(True)
    plt.figure()
    plt.plot(xs, np.array([alpha(x, 0) for x in xs]), marker='o', label=r'$\alpha(x)$')
    plt.plot(xs, np.array([u(x, 0) for x in xs]), label=r'u(x, 0)')
    plt.legend()
    # plt.ylim(0., 1.)
    plt.savefig(os.path.join(outdir, 'profile.pdf'))

    return time_data_pd, outdir
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
예제 #7
0
def traction_1d(
    outdir="outdir",
    configString='',
    parameters='',
):

    parameters_file = parameters

    savelag = 1

    config = json.loads(configString) if configString != '' else ''

    with open('../parameters/form_compiler.yml') as f:
        form_compiler_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/solvers_default.yml') as f:
        solver_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/model1d.yaml') as f:
        material_parameters = yaml.load(f, Loader=yaml.FullLoader)['material']

    with open('../parameters/loading.yaml') as f:
        timestepping_parameters = yaml.load(f,
                                            Loader=yaml.FullLoader)['loading']

    with open('../parameters/stability.yaml') as f:
        stability_parameters = yaml.load(f,
                                         Loader=yaml.FullLoader)['stability']

    # with open('../parameters/geometry.yaml') as f:
    geometry_parameters = {'Lx': 1., 'n': 3}

    # default values
    parameters = {
        'solver': {
            **solver_parameters
        },
        'compiler': {
            **form_compiler_parameters
        },
        'loading': {
            **timestepping_parameters
        },
        'stability': {
            **stability_parameters
        },
        'material': {
            **material_parameters
        },
        'geometry': {
            **geometry_parameters
        }
    }

    # command line pointer parameter file

    if parameters_file:
        with open(parameters_file) as f:
            parameters = yaml.load(f, Loader=yaml.FullLoader)

    # command line config, highest priority

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

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir += '-{}'.format(signature)

    print(parameters)

    # outdir += '-{}'.format(cmd_parameters['loading']['postfix'])
    parameters['loading']['outdir'] = outdir
    Path(outdir).mkdir(parents=True, exist_ok=True)

    print('Outdir is: ' + outdir)

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)

    with open(os.path.join(outdir, 'rerun.sh'), 'w') as f:
        configuration = deepcopy(parameters)
        configuration['loading'].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)

    Lx = parameters['geometry']['Lx']
    n = parameters['geometry']['n']
    ell = parameters['material']['ell']

    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)

    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': u, 'alpha': 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 = [
        dolfin.DirichletBC(V_alpha, dolfin.Constant(0), left),
        dolfin.DirichletBC(V_alpha, dolfin.Constant(0), right)
    ]

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}
    # bcs_alpha = [dolfin.DirichletBC(V_alpha, dolfin.Constant(1.), right)]

    # Files for output
    log(LogLevel.WARNING, '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
    # import pdb; pdb.set_trace()
    w_1 = material_parameters['sigma_D0']**2 / material_parameters['E']
    w = w_1 * alpha
    eps = u.dx(0)

    # sigma = material_parameters['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 = DamageElasticityModel1D(state, material_parameters)
    model.dx = dx
    # energy = model.total_energy_density(u, alpha)*model.dx
    energy = 1. / 2. * material_parameters['E'] * a * eps**2. * dx + w_1 * (
        alpha + material_parameters['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.EquilibriumSolver(energy,
                                       state,
                                       bcs,
                                       parameters=solver_parameters)

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

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

    time_data = []

    linesearch = LineSearch(energy, state)
    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):
        ut.t = load
        # alpha_old.assign(alpha)
        # import pdb; pdb.set_trace()

        log(LogLevel.CRITICAL,
            'CRITICAL: Solving load t = {:.2f}'.format(load))
        (time_data_i, am_iter) = solver.solve()
        # (stable, negev) = stability.solve(solver.damage_solver.problem.lb)

        # log(LogLevel.INFO, 'INFO: Current state is{}stable'.format(' ' if stable else ' un'))
        # import pdb; pdb.set_trace()
        solver.update()

        # mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        # log(LogLevel.INFO, 'INFO: lmbda min {}'.format(lmbda_min_prev))
        # log(LogLevel.INFO, 'INFO: mineig {}'.format(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
        #     log(LogLevel.PROGRESS, 'About to bifurcate load {} step {}'.format(load, 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. * material_parameters['E'] * a * eps**2. * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * material_parameters['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)
        # import pdb; pdb.set_trace()
        log(
            LogLevel.CRITICAL,
            "Load/time step {:.4g}: iteration: {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"], time_data_i["iterations"][0],
                time_data_i["alpha_error"][0]))

        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)
                log(LogLevel.PROGRESS, 'PROGRESS: written step {}'.format(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(time_data_pd['stable'])

    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'))
    # # 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 numerical_test(
    user_parameters,
    ell=0.05,
    nu=0.,
):
    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    bifurcation_loads = []

    # Create mesh and define function space
    geometry_parameters = {'Lx': 1., 'Ly': .1, 'n': 5}

    # Define Dirichlet boundaries
    outdir = '../test/output/test_secondorderevo'
    Path(outdir).mkdir(parents=True, exist_ok=True)

    with open('../parameters/form_compiler.yml') as f:
        form_compiler_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/solvers_default.yml') as f:
        solver_parameters = yaml.load(f, Loader=yaml.FullLoader)

    with open('../parameters/model1d.yaml') as f:
        material_parameters = yaml.load(f, Loader=yaml.FullLoader)['material']

    with open('../parameters/loading.yaml') as f:
        loading_parameters = yaml.load(f, Loader=yaml.FullLoader)['loading']

    with open('../parameters/stability.yaml') as f:
        stability_parameters = yaml.load(f,
                                         Loader=yaml.FullLoader)['stability']

    Path(outdir).mkdir(parents=True, exist_ok=True)

    print('Outdir is: ' + outdir)

    default_parameters = {
        'code': {
            **code_parameters
        },
        'compiler': {
            **form_compiler_parameters
        },
        'geometry': {
            **geometry_parameters
        },
        'loading': {
            **loading_parameters
        },
        'material': {
            **material_parameters
        },
        'solver': {
            **solver_parameters
        },
        'stability': {
            **stability_parameters
        },
    }

    default_parameters.update(user_parameters)
    # FIXME: Not nice
    parameters = default_parameters

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)

    Lx = parameters['geometry']['Lx']
    Ly = parameters['geometry']['Ly']
    ell = parameters['material']['ell']
    comm = MPI.comm_world
    geom = mshr.Rectangle(dolfin.Point(-Lx / 2., -Ly / 2.),
                          dolfin.Point(Lx / 2., Ly / 2.))
    # import pdb; pdb.set_trace()
    # resolution = max(geometry_parameters['n'] * Lx / ell, 1/(Ly*10))
    resolution = max(geometry_parameters['n'] * Lx / ell, 5 / (Ly * 10))
    resolution = 50
    mesh = mshr.generate_mesh(geom, resolution)
    meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
    meshf << mesh
    plot(mesh)
    plt.savefig(os.path.join(outdir, "mesh.pdf"), bbox_inches='tight')

    savelag = 1
    left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    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)

    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 = Function(V_alpha)
    dalpha = TrialFunction(V_alpha)
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)

    state = {'u': u, 'alpha': 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), left_bottom_pt, method="pointwise")
    ]

    bcs_alpha_l = DirichletBC(V_alpha, Constant(0.0), left)
    bcs_alpha_r = DirichletBC(V_alpha, Constant(0.0), right)
    # bcs_alpha =[bcs_alpha_l, bcs_alpha_r]
    bcs_alpha = []

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}

    # import pdb; pdb.set_trace()

    ell = parameters['material']['ell']

    # Problem definition
    # Problem definition
    k_res = parameters['material']['k_res']
    a = (1 - alpha)**2. + k_res
    w_1 = parameters['material']['sigma_D0']**2 / parameters['material']['E']
    w = w_1 * alpha
    eps = sym(grad(u))
    lmbda0 = parameters['material']['E'] * parameters['material']['nu'] / (
        1. - parameters['material']['nu'])**2.
    mu0 = parameters['material']['E'] / 2. / (1.0 +
                                              parameters['material']['nu'])
    Wu = 1. / 2. * lmbda0 * tr(eps)**2. + mu0 * inner(eps, eps)

    energy = a * Wu * dx + w_1 *( alpha + \
            parameters['material']['ell']** 2.*inner(grad(alpha), grad(alpha)))*dx

    eps_ = variable(eps)
    sigma = diff(a * Wu, eps_)
    e1 = dolfin.Constant([1, 0])

    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_out.parameters["functions_share_mesh"] = True
    file_out.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_eig = dolfin.XDMFFile(os.path.join(outdir, "modes.xdmf"))
    file_eig.parameters["functions_share_mesh"] = True
    file_eig.parameters["flush_output"] = True
    file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation.xdmf"))
    file_bif.parameters["functions_share_mesh"] = True
    file_bif.parameters["flush_output"] = True
    file_bif_postproc = dolfin.XDMFFile(
        os.path.join(outdir, "bifurcation_postproc.xdmf"))
    file_bif_postproc.parameters["functions_share_mesh"] = True
    file_bif_postproc.parameters["flush_output"] = True

    solver = EquilibriumAM(energy, state, bcs, parameters=parameters['solver'])
    stability = StabilitySolver(energy,
                                state,
                                bcs,
                                parameters=parameters['stability'])
    linesearch = LineSearch(energy, state)

    xs = np.linspace(-parameters['geometry']['Lx'] / 2.,
                     parameters['geometry']['Lx'] / 2, 50)

    load_steps = np.linspace(parameters['loading']['load_min'],
                             parameters['loading']['load_max'],
                             parameters['loading']['n_steps'])
    log(LogLevel.INFO, '====================== EVO ==========================')
    log(LogLevel.INFO, '{}'.format(parameters))

    for it, load in enumerate(load_steps):
        log(LogLevel.CRITICAL,
            '====================== STEPPING ==========================')
        log(LogLevel.CRITICAL,
            'CRITICAL: Solving load t = {:.2f}'.format(load))
        ut.t = load
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions

        (stable, negev) = stability.solve(solver.damage.problem.lb)
        log(LogLevel.CRITICAL,
            'Current state is{}stable'.format(' ' if stable else ' un'))

        # we postpone the update after the stability check
        solver.update()

        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        log(LogLevel.INFO, 'INFO: lmbda min {}'.format(lmbda_min_prev))
        log(LogLevel.INFO, 'INFO: mineig {}'.format(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.

        time_data_i["load"] = load
        time_data_i["alpha_max"] = max(alpha.vector()[:])
        time_data_i["elastic_energy"] = dolfin.assemble(
            1. / 2. * material_parameters['E'] * a * eps**2. * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * material_parameters['ell']**2. *
             inner(grad(alpha), grad(alpha))) * dx)
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf

        snn = dolfin.dot(dolfin.dot(sigma, e1), e1)
        time_data_i["sigma"] = 1 / parameters['geometry'][
            'Ly'] * dolfin.assemble(snn * ds(1))

        log(
            LogLevel.INFO,
            "Load/time step {:.4g}: iteration: {:3d}, err_alpha={:.4g}".format(
                time_data_i["load"], time_data_i["iterations"][0],
                time_data_i["alpha_error"][0]))

        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)
                log(LogLevel.PROGRESS, 'PROGRESS: written step {}'.format(it))

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

        spacetime.append(get_trace(alpha))

        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_postproc as file:
                # leneigs = len(modes)
                # maxmodes = min(3, leneigs)
                beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                log(
                    LogLevel.DEBUG,
                    'DEBUG: irrev {}'.format(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)

    _spacetime = pd.DataFrame(spacetime)
    spacetime = _spacetime.fillna(0)
    mat = np.matrix(spacetime)
    plt.imshow(mat, cmap='Greys', vmin=0., vmax=1., aspect=.1)
    plt.colorbar()

    def format_space(x, pos, xresol=100):
        return '$%1.1f$' % ((-x + xresol / 2) / xresol)

    def format_time(t, pos, xresol=100):
        return '$%1.1f$' % ((t - parameters['loading']['load_min']) /
                            parameters['loading']['n_steps'] *
                            parameters['loading']['load_max'])

    from matplotlib.ticker import FuncFormatter, MaxNLocator

    ax = plt.gca()

    ax.yaxis.set_major_formatter(FuncFormatter(format_space))
    ax.xaxis.set_major_formatter(FuncFormatter(format_time))

    plt.xlabel('$x$')
    plt.ylabel('$t$')
    plt.savefig(os.path.join(outdir, "spacetime.pdf".format(load)),
                bbox_inches="tight")

    spacetime.to_json(os.path.join(outdir + "/spacetime.json"))

    from matplotlib.ticker import FuncFormatter, MaxNLocator
    plot(alpha)
    plt.savefig(os.path.join(outdir, 'alpha.pdf'))
    log(LogLevel.INFO,
        "Saved figure: {}".format(os.path.join(outdir, 'alpha.pdf')))

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

    return time_data_pd, outdir
예제 #9
0
def numerical_test(user_parameters):
    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    bifurcation_loads = []

    # Create mesh and define function space
    # Define Dirichlet boundaries
    comm = MPI.comm_world

    default_parameters = getDefaultParameters()
    default_parameters.update(user_parameters)
    # FIXME: Not nice
    parameters = default_parameters
    parameters['code']['script'] = __file__
    # import pdb; pdb.set_trace()

    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir = '../output/traction/{}-{}CPU'.format(signature, size)
    Path(outdir).mkdir(parents=True, exist_ok=True)

    log(LogLevel.INFO, 'INFO: Outdir is: ' + outdir)
    BASE_DIR = os.path.dirname(os.path.realpath(__file__))
    print(parameters['geometry'])
    d = {
        'Lx': parameters['geometry']['Lx'],
        'Ly': parameters['geometry']['Ly'],
        'h': parameters['material']['ell'] / parameters['geometry']['n']
    }

    geom_signature = hashlib.md5(str(d).encode('utf-8')).hexdigest()

    # --------------------------------------------------------
    # Mesh creation with gmsh
    Lx = parameters['geometry']['Lx']
    Ly = parameters['geometry']['Ly']
    n = parameters['geometry']['n']
    ell = parameters['material']['ell']
    fname = os.path.join('../meshes', 'strip-{}'.format(geom_signature))

    resolution = max(parameters['geometry']['n'] * Lx / ell, 5 / (Ly * 10))
    resolution = 3

    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)))
    mesh = mshr.generate_mesh(geom, resolution)

    log(
        LogLevel.INFO, 'Number of dofs: {}'.format(
            mesh.num_vertices() * (1 + parameters['general']['dim'])))
    if size == 1:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        plot(mesh)
        plt.savefig(os.path.join(outdir, "mesh.pdf"), bbox_inches='tight')

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)

    Lx = parameters['geometry']['Lx']
    ell = parameters['material']['ell']
    savelag = 1
    # mf = dolfin.MeshFunction("size_t", mesh, 1, 0)

    # Function Spaces
    V_u = dolfin.VectorFunctionSpace(mesh, "CG", 1)
    V_alpha = dolfin.FunctionSpace(mesh, "CG", 1)
    L2 = dolfin.FunctionSpace(mesh, "DG", 0)
    u = dolfin.Function(V_u, name="Total displacement")
    u.rename('u', 'u')
    alpha = Function(V_alpha)
    alpha_old = dolfin.Function(alpha.function_space())
    alpha.rename('alpha', 'alpha')
    dalpha = TrialFunction(V_alpha)
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)

    state = {'u': u, 'alpha': alpha}
    Z = dolfin.FunctionSpace(
        mesh, dolfin.MixedElement([u.ufl_element(),
                                   alpha.ufl_element()]))
    z = dolfin.Function(Z)
    v, beta = dolfin.split(z)
    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)
    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 = []

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}

    ds = dolfin.Measure("ds", subdomain_data=mf)
    dx = dolfin.Measure("dx", metadata=parameters['compiler'], domain=mesh)

    ell = parameters['material']['ell']

    # -----------------------
    # Problem definition
    k_res = parameters['material']['k_res']
    a = (1 - alpha)**2. + k_res
    w_1 = parameters['material']['sigma_D0']**2 / parameters['material']['E']
    w = w_1 * alpha
    eps = sym(grad(u))
    eps0t = Expression([['t', 0.], [0., 't']], t=0., degree=0)
    lmbda0 = parameters['material']['E'] * parameters['material']['nu'] / (
        1. - parameters['material']['nu'])**2.
    mu0 = parameters['material']['E'] / 2. / (1.0 +
                                              parameters['material']['nu'])
    nu = parameters['material']['nu']
    sigma0 = lmbda0 * tr(eps) * dolfin.Identity(
        parameters['general']['dim']) + 2 * mu0 * eps
    e1 = Constant((1., 0))
    _sigma = ((1 - alpha)**2. + k_res) * sigma0
    _snn = dolfin.dot(dolfin.dot(_sigma, e1), e1)

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

    ell = parameters['material']['ell']
    E = parameters['material']['E']

    def elastic_energy(u, alpha, E=E, nu=nu, eps0t=eps0t, k_res=k_res):
        a = (1 - alpha)**2. + k_res
        eps = sym(grad(u))
        Wt = a*E*nu/(2*(1-nu**2.)) * tr(eps)**2.                                \
            + a*E/(2.*(1+nu))*(inner(eps, eps))
        return Wt * dx

    def dissipated_energy(alpha, w_1=w_1, ell=ell):
        return w_1 * (alpha + ell**2. * inner(grad(alpha), grad(alpha))) * dx

    def total_energy(u,
                     alpha,
                     k_res=k_res,
                     w_1=w_1,
                     E=E,
                     nu=nu,
                     ell=ell,
                     eps0t=eps0t):
        elastic_energy_ = elastic_energy(u,
                                         alpha,
                                         E=E,
                                         nu=nu,
                                         eps0t=eps0t,
                                         k_res=k_res)
        dissipated_energy_ = dissipated_energy(alpha, w_1=w_1, ell=ell)
        return elastic_energy_ + dissipated_energy_

    energy = total_energy(u, alpha)

    # Hessian = derivative(derivative(Wppt*dx, z, TestFunction(Z)), z, TrialFunction(Z))

    def create_output(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_postproc = dolfin.XDMFFile(
            os.path.join(outdir, "postprocess.xdmf"))
        file_postproc.parameters["functions_share_mesh"] = True
        file_postproc.parameters["flush_output"] = True
        file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
        file_eig.parameters["functions_share_mesh"] = True
        file_eig.parameters["flush_output"] = True
        file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation.xdmf"))
        file_bif.parameters["functions_share_mesh"] = True
        file_bif.parameters["flush_output"] = True
        file_bif_postproc = dolfin.XDMFFile(
            os.path.join(outdir, "bifurcation_postproc.xdmf"))
        file_bif_postproc.parameters["functions_share_mesh"] = True
        file_bif_postproc.parameters["flush_output"] = True
        file_ealpha = dolfin.XDMFFile(os.path.join(outdir, "elapha.xdmf"))
        file_ealpha.parameters["functions_share_mesh"] = True
        file_ealpha.parameters["flush_output"] = True

        files = {
            'output': file_out,
            'postproc': file_postproc,
            'eigen': file_eig,
            'bifurcation': file_bif,
            'ealpha': file_ealpha
        }

        return files

    files = create_output(outdir)

    solver = EquilibriumAM(energy, state, bcs, parameters=parameters)
    stability = StabilitySolver(energy, state, bcs, parameters=parameters)
    linesearch = LineSearch(energy, state)

    load_steps = np.linspace(parameters['loading']['load_min'],
                             parameters['loading']['load_max'],
                             parameters['loading']['n_steps'])

    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    bifurcation_loads = []
    to_remove = []

    perturb = False
    from matplotlib import cm

    log(LogLevel.INFO, '{}'.format(parameters))
    for step, load in enumerate(load_steps):
        plt.clf()
        mineigs = []
        exhaust_modes = []

        log(LogLevel.CRITICAL,
            '====================== STEPPING ==========================')
        log(LogLevel.CRITICAL,
            'CRITICAL: Solving load t = {:.2f}'.format(load))
        alpha_old.assign(alpha)
        ut.t = load
        # (time_data_i, am_iter) = solver.solve(outdir)
        (time_data_i, am_iter) = solver.solve()

        # Second order stability conditions
        (stable, negev) = stability.solve(solver.damage.problem.lb)

        log(LogLevel.CRITICAL,
            'Current state is{}stable'.format(' ' if stable else ' un'))

        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        # log(LogLevel.INFO, 'INFO: lmbda min {}'.format(lmbda_min_prev))
        log(LogLevel.INFO, 'INFO: mineig {:.5e}'.format(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
            log(
                LogLevel.INFO,
                'INFO: About to bifurcate load {:.3f} step {}'.format(
                    load, step))
            bifurcation_loads.append(load)
            modes = np.where(stability.eigs < 0)[0]

            bifurc_i += 1

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

        # we postpone the update after the stability check
        if stable:
            solver.update()
            log(
                LogLevel.INFO, '    Current state is{}stable'.format(
                    ' ' if stable else ' un'))
        else:
            # Continuation
            iteration = 1
            mineigs.append(stability.mineig)

            while stable == False:
                log(LogLevel.INFO,
                    'Continuation iteration {}'.format(iteration))
                plt.close('all')
                pert = [(_v, _b) for _v, _b in zip(
                    stability.perturbations_v, stability.perturbations_beta)]
                _nmodes = len(pert)
                en_vars = []
                h_opts = []
                hbounds = []
                en_perts = []

                for i, mode in enumerate(pert):
                    h_opt, bounds, enpert, en_var = linesearch.search(
                        {
                            'u': u,
                            'alpha': alpha,
                            'alpha_old': alpha_old
                        }, mode[0], mode[1])
                    h_opts.append(h_opt)
                    en_vars.append(en_var)
                    hbounds.append(bounds)
                    en_perts.append(enpert)

                if rank == 0:
                    # fig = plt.figure(dpi=80, facecolor='w', edgecolor='k')
                    # plt.subplot(1, 4, 1)
                    # plt.set_cmap('binary')
                    # # dolfin.plot(mesh, alpha = 1.)
                    # dolfin.plot(
                    #     project(stability.inactivemarker1, L2), alpha = 1., vmin=0., vmax=1.)
                    # plt.title('derivative zero')
                    # plt.subplot(1, 4, 2)
                    # # dolfin.plot(mesh, alpha = .5)
                    # dolfin.plot(
                    #     project(stability.inactivemarker2, L2), alpha = 1., vmin=0., vmax=1.)
                    # plt.title('ub tolerance')
                    # plt.subplot(1, 4, 3)
                    # # dolfin.plot(mesh, alpha = .5)
                    # dolfin.plot(
                    #     project(stability.inactivemarker3, L2), alpha = 1., vmin=0., vmax=1.)
                    # plt.title('alpha-alpha_old')
                    # plt.subplot(1, 4, 4)
                    # # dolfin.plot(mesh, alpha = .5)
                    # dolfin.plot(
                    #     project(stability.inactivemarker4, L2), alpha = 1., vmin=0., vmax=1.)
                    # plt.title('intersec deriv, ub')
                    # plt.savefig(os.path.join(outdir, "inactivesets-{:.3f}-{:d}.pdf".format(load, iteration)))

                    # plt.set_cmap('hot')

                    fig = plt.figure(dpi=80, facecolor='w', edgecolor='k')

                    for i, mode in enumerate(pert):
                        plt.subplot(2, _nmodes + 1, i + 2)
                        plt.axis('off')
                        plot(mode[1], cmap=cm.ocean)

                        plt.title(
                            'mode {} $h^*$={:.3f}\n $\\lambda_{}$={:.3e} \n $\\Delta E$={:.3e}'
                            .format(i, h_opts[i], i, stability.eigs[i],
                                    en_vars[i]),
                            fontsize=15)

                        # plt.title('mode {}'
                        #     .format(i), fontsize= 15)

                        plt.subplot(2, _nmodes + 1, _nmodes + 2 + 1 + i)
                        plt.axis('off')
                        _pert_beta = mode[1]
                        _pert_v = mode[0]

                        if hbounds[i][0] == hbounds[i][1] == 0:
                            plt.plot(hbounds[i][0], 0)
                        else:
                            hs = np.linspace(hbounds[i][0], hbounds[i][1], 100)
                            z = np.polyfit(
                                np.linspace(hbounds[i][0], hbounds[i][1],
                                            len(en_perts[i])), en_perts[i],
                                parameters['stability']['order'])
                            p = np.poly1d(z)
                            plt.plot(hs, p(hs), c='k')
                            plt.plot(np.linspace(hbounds[i][0], hbounds[i][1],
                                                 len(en_perts[i])),
                                     en_perts[i],
                                     marker='o',
                                     markersize=10,
                                     c='k')
                            # import pdb; pdb.set_trace()
                            plt.plot(hs,
                                     stability.eigs[i] * hs**2,
                                     c='r',
                                     lw=.3)
                            plt.axvline(h_opts[i], lw=.3, c='k')
                            plt.axvline(0, lw=2, c='k')
                        # plt.title('{}'.format(i))
                        plt.tight_layout(h_pad=1.5, pad=1.5)
                    # plt.legend()
                    plt.savefig(
                        os.path.join(
                            outdir,
                            "modes-{:.3f}-{}.pdf".format(load, iteration)))
                    plt.close(fig)
                    plt.clf()
                    log(LogLevel.INFO, 'plotted modes')

                cont_data_pre = compile_continuation_data(state, energy)

                log(LogLevel.INFO,
                    'Estimated energy variation {:.3e}'.format(en_var))

                Ealpha = Function(V_alpha)
                Ealpha.vector()[:] = assemble(stability.inactiveEalpha)[:]
                Ealpha.rename('Ealpha-{}'.format(iteration),
                              'Ealpha-{}'.format(iteration))

                with files['ealpha'] as file:
                    file.write(Ealpha, load)

                save_current_bifurcation = True

                # pick the first of the non exhausted modes-
                non_zero_h = np.where(abs(np.array(h_opts)) > DOLFIN_EPS)[0]
                log(LogLevel.INFO, 'Nonzero h {}'.format(non_zero_h))
                avail_modes = set(non_zero_h) - set(exhaust_modes)

                opt_mode = 0
                # opt_mode = np.argmin(en_vars)
                log(LogLevel.INFO, 'Energy vars {}'.format(en_vars))
                log(
                    LogLevel.INFO, 'Pick bifurcation mode {} out of {}'.format(
                        opt_mode, len(en_vars)))
                # h_opt = min(h_opts[opt_mode],1.e-2)
                h_opt = h_opts[opt_mode]
                perturbation_v = stability.perturbations_v[opt_mode]
                perturbation_beta = stability.perturbations_beta[opt_mode]
                minmode = stability.minmode
                (perturbation_v,
                 perturbation_beta) = minmode.split(deepcopy=True)

                # (perturbation_v, perturbation_beta) = stability.perturbation_v, stability.perturbation_beta

                def energy_1d(h):
                    #return assemble(energy_functional(u + h * perturbation_v, alpha + h * perturbation_beta))
                    u_ = Function(u.function_space())
                    alpha_ = Function(alpha.function_space())
                    u_.vector(
                    )[:] = u.vector()[:] + h * perturbation_v.vector()[:]
                    alpha_.vector()[:] = alpha.vector(
                    )[:] + h * perturbation_beta.vector()[:]
                    u_.vector().vec().ghostUpdate()
                    alpha_.vector().vec().ghostUpdate()
                    return assemble(total_energy(u_, alpha_))

                (hmin, hmax) = linesearch.admissible_interval(
                    alpha, alpha_old, perturbation_beta)
                hs = np.linspace(hmin, hmax, 20)
                energy_vals = np.array([energy_1d(h) for h in hs])
                stability.solve(solver.damage.problem.lb)

                Hzz = assemble(stability.H * minmode * minmode)
                Gz = assemble(stability.J * minmode)
                mineig_z = Hzz / assemble(dot(minmode, minmode) * dx)

                energy_vals_quad = energy_1d(0) + hs * Gz + hs**2 * Hzz / 2
                # h_opt = hs[np.argmin(energy_vals)]
                print('computed h_opt {}'.format(hs[np.argmin(energy_vals)]))
                print("%%%%%%%%% ", mineig_z, "-", mineig)

                if rank == 0:
                    plt.figure()
                    # plt.plot(hs,energy_vals, marker = 'o')
                    plt.plot(hs, energy_vals, marker='o', label="exact")
                    plt.plot(hs,
                             energy_vals_quad,
                             label="quadratic approximation")
                    plt.legend()
                    plt.title("eig {:.4f} vs {:.4f} expected".format(
                        mineig_z, mineig))
                    plt.axvline(h_opt)
                    # import pdb; pdb.set_trace()
                    plt.savefig(
                        os.path.join(outdir,
                                     "energy1d-{:.3f}.pdf".format(load)))

                iteration += 1
                log(LogLevel.CRITICAL, 'Bifurcating')

                save_current_bifurcation = True
                alpha_bif.assign(alpha)
                alpha_bif_old.assign(alpha_old)

                # admissible perturbation
                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()

                log(LogLevel.INFO,
                    'min a+h_opt beta_{} = {}'.format(opt_mode, min(aval)))
                log(LogLevel.INFO,
                    'max a+h_opt beta_{} = {}'.format(opt_mode, max(aval)))
                log(LogLevel.INFO, 'Solving equilibrium from perturbed state')
                (time_data_i, am_iter) = solver.solve(outdir)
                # (time_data_i, am_iter) = solver.solve()
                log(LogLevel.INFO, 'Checking stability of new state')
                (stable, negev) = stability.solve(solver.damage.problem.lb)
                mineigs.append(stability.mineig)

                log(
                    LogLevel.INFO,
                    'Continuation iteration {}, current state is{}stable'.
                    format(iteration, ' ' if stable else ' un'))

                cont_data_post = compile_continuation_data(state, energy)
                DeltaE = (cont_data_post['energy'] - cont_data_pre['energy'])
                relDeltaE = (cont_data_post['energy'] -
                             cont_data_pre['energy']) / cont_data_pre['energy']
                release = DeltaE < 0 and np.abs(
                    DeltaE) > parameters['stability']['cont_rtol']

                log(
                    LogLevel.INFO,
                    'Continuation: post energy {} - pre energy {}'.format(
                        cont_data_post['energy'], cont_data_pre['energy']))
                log(
                    LogLevel.INFO,
                    'Actual absolute energy variation Delta E = {:.7e}'.format(
                        DeltaE))
                log(
                    LogLevel.INFO,
                    'Actual relative energy variation relDelta E = {:.7e}'.
                    format(relDeltaE))
                log(LogLevel.INFO,
                    'Iter {} mineigs = {}'.format(iteration, mineigs))

                if rank == 0:
                    plt.figure()
                    plt.plot(mineigs, marker='o')
                    plt.axhline(0.)
                    plt.savefig(
                        os.path.join(outdir,
                                     "mineigs-{:.3f}.pdf".format(load)))

                # continuation criterion
                if abs(np.diff(mineigs)[-1]) > 1e-10:
                    log(LogLevel.INFO,
                        'Min eig change = {:.3e}'.format(np.diff(mineigs)[-1]))
                    log(LogLevel.INFO, 'Continuing perturbations')
                else:
                    log(LogLevel.INFO,
                        'Min eig change = {:.3e}'.format(np.diff(mineigs)[-1]))
                    log(LogLevel.CRITICAL, 'We are stuck in the matrix')
                    log(LogLevel.WARNING, 'Exploring next mode')
                    exhaust_modes.append(opt_mode)
                    # import pdb; pdb.set_trace()
                    log(LogLevel.WARNING, 'Continuing load program')
                    break
                    #
                # if not release:
                # log(LogLevel.CRITICAL, 'Small nergy release , we are stuck in the matrix')
                # log(LogLevel.CRITICAL, 'No decrease in energy, we are stuck in the matrix')
                # log(LogLevel.WARNING, 'Continuing load program')
                # import pdb; pdb.set_trace()
                # break
                # else:
                #     # warn
                #     log(LogLevel.CRITICAL, 'Found zero increment, we are stuck in the matrix')
                #     log(LogLevel.WARNING, 'Exploring next mode')
                #     exhaust_modes.append(opt_mode)
                #     import pdb; pdb.set_trace()
                #     # log(LogLevel.WARNING, 'Continuing load program')
                #     # break

            solver.update()
            log(LogLevel.INFO,
                'bifurcation loads : {}'.format(bifurcation_loads))
            np.save(os.path.join(outdir, 'bifurcation_loads'),
                    bifurcation_loads,
                    allow_pickle=True,
                    fix_imports=True)

            if save_current_bifurcation:
                time_data_i['h_opt'] = h_opt
                time_data_i['max_h'] = hbounds[opt_mode][1]
                time_data_i['min_h'] = hbounds[opt_mode][0]

                modes = np.where(stability.eigs < 0)[0]
                leneigs = len(modes)
                maxmodes = min(3, leneigs)

                with files['bifurcation'] as file:
                    for n in range(len(pert)):
                        mode = dolfin.project(stability.perturbations_beta[n],
                                              V_alpha)
                        modename = 'beta-%d' % n
                        mode.rename(modename, modename)
                        log(LogLevel.INFO, 'Saved mode {}'.format(modename))
                        file.write(mode, load)

                # with files['file_bif_postproc'] as file:
                # leneigs = len(modes)
                # maxmodes = min(3, leneigs)
                # beta0v = dolfin.project(stability.perturbation_beta, V_alpha)
                # log(LogLevel.DEBUG, 'DEBUG: irrev {}'.format(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'),
                        en_perts,
                        allow_pickle=True,
                        fix_imports=True)

                with files['eigen'] 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')
                    file.write(_v, load)
                    file.write(_beta, load)

                # save_current_bifurcation = False

        time_data_i["load"] = load
        time_data_i["alpha_max"] = max(alpha.vector()[:])
        time_data_i["elastic_energy"] = dolfin.assemble(
            elastic_energy(u, alpha, E=E, nu=nu, eps0t=eps0t, k_res=k_res))
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * parameters['material']['ell']**2. *
             inner(grad(alpha), grad(alpha))) * dx)
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf
        time_data_i["sigma"] = 1 / Ly * dolfin.assemble(_snn * ds(1))
        # import pdb; pdb.set_trace()

        log(
            LogLevel.INFO,
            "Load/time step {:.4g}: converged in iterations: {:3d}, err_alpha={:.4e}"
            .format(time_data_i["load"], time_data_i["iterations"][0],
                    time_data_i["alpha_error"][0]))

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

        with files['output'] as file:
            file.write(alpha, load)
            file.write(u, load)

        with files['postproc'] as file:
            file.write_checkpoint(alpha,
                                  "alpha-{}".format(step),
                                  step,
                                  append=True)
            file.write_checkpoint(u, "u-{}".format(step), step, append=True)
            log(LogLevel.INFO,
                'INFO: written postprocessing step {}'.format(step))

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

        if rank == 0:
            # plt.clf()
            # if load>1.1:
            # import pdb; pdb.set_trace()
            # plt.plot(time_data_i["alpha_error"],  marker='o')
            # plt.title('error, criterion: {}'.format(parameters['equilibrium']['criterion']))
            # plt.axhline(parameters['equilibrium']['tol'])
            # plt.savefig(os.path.join(outdir, 'errors-{}.pdf'.format(step)))
            # plt.clf()
            # plt.colorbar(plot(alpha))
            # fig = plt.figure()
            # plot(alpha)
            # plt.savefig(os.path.join(outdir, 'alpha.pdf'))
            # log(LogLevel.INFO, "Saved figure: {}".format(os.path.join(outdir, 'alpha.pdf')))
            plt.close('all')

            fig = plt.figure()
            for i, d in enumerate(time_data_pd['eigs']):
                # if d is not (np.inf or np.nan or float('inf')):
                if np.isfinite(d).all():
                    lend = len(d) if isinstance(d, np.ndarray) else 1
                    plt.scatter([(time_data_pd['load'].values)[i]] * lend,
                                d,
                                c=np.where(np.array(d) < 0., 'red', 'black'))

            plt.axhline(0, c='k', lw=2.)
            plt.xlabel('t')
            # [plt.axvline(b) for b in bifurcation_loads]
            # import pdb; pdb.set_trace()
            log(LogLevel.INFO,
                'Spectrum bifurcation loads : {}'.format(bifurcation_loads))
            plt.xticks(list(plt.xticks()[0]) + bifurcation_loads)
            [plt.axvline(bif, lw=2, c='k') for bif in bifurcation_loads]
            plt.savefig(os.path.join(outdir, "spectrum.pdf"),
                        bbox_inches='tight')
        # plt.plot()

    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
예제 #11
0
def numerical_test(
    user_parameters,
    ell=0.05,
    nu=0.,
):
    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    bifurcation_loads = []

    # Create mesh and define function space
    # geometry_parameters = {'R': 1., 'n': 5}

    # Define Dirichlet boundaries
    comm = MPI.comm_world

    with open('../parameters/form_compiler.yml') as f:
        form_compiler_parameters = yaml.load(f, Loader=yaml.FullLoader)
    with open('../parameters/solvers_default.yml') as f:
        solver_parameters = yaml.load(f, Loader=yaml.FullLoader)
    with open('../parameters/solvers_default.yml') as f:
        damage_parameters = yaml.load(f, Loader=yaml.FullLoader)['damage']
    with open('../parameters/solvers_default.yml') as f:
        elasticity_parameters = yaml.load(f,
                                          Loader=yaml.FullLoader)['elasticity']
    with open('../parameters/film.yaml') as f:
        material_parameters = yaml.load(f, Loader=yaml.FullLoader)['material']
    with open('../parameters/loading.yaml') as f:
        loading_parameters = yaml.load(f, Loader=yaml.FullLoader)['loading']
    with open('../parameters/stability.yaml') as f:
        stability_parameters = yaml.load(f,
                                         Loader=yaml.FullLoader)['stability']
    with open('../parameters/stability.yaml') as f:
        inertia_parameters = yaml.load(f, Loader=yaml.FullLoader)['inertia']
    with open('../parameters/stability.yaml') as f:
        eigen_parameters = yaml.load(f, Loader=yaml.FullLoader)['eigen']

    # import pdb; pdb.set_trace()

    default_parameters = {
        'code': {
            **code_parameters
        },
        'compiler': {
            **form_compiler_parameters
        },
        'eigen': {
            **eigen_parameters
        },
        # 'geometry': {**geometry_parameters},
        'inertia': {
            **inertia_parameters
        },
        'loading': {
            **loading_parameters
        },
        'material': {
            **material_parameters
        },
        'solver': {
            **solver_parameters
        },
        'stability': {
            **stability_parameters
        },
        'elasticity': {
            **elasticity_parameters
        },
        'damage': {
            **damage_parameters
        },
    }

    default_parameters.update(user_parameters)
    # FIXME: Not nice
    parameters = default_parameters
    signature = hashlib.md5(str(parameters).encode('utf-8')).hexdigest()
    outdir = '../test/output/film2d/{}'.format(signature)
    # outdir = '../test/output/test_film2d-init-{}'.format(signature)
    Path(outdir).mkdir(parents=True, exist_ok=True)
    log(LogLevel.INFO, 'INFO: Outdir is: ' + outdir)

    R = parameters['geometry']['R']

    #   Mesh1 ==================================
    # geom = mshr.Circle(dolfin.Point(0., 0.), R)
    # resolution = max(geometry_parameters['n'] * Lx / ell, 1/(Ly*10))
    # resolution = max(parameters['geometry']['n'] * R / ell, (10/R))
    # resolution = 10
    # log(LogLevel.INFO, 'mesh resolution {}'.format(resolution))
    # mesh = mshr.generate_mesh(geom,  resolution)
    #   Mesh2 ==================================
    # mesh = dolfin.Mesh("../scripts/meshes/diskR3-mesh.xml")
    #   Mesh3 ==================================
    # fname = os.path.join('../meshes', 'circle-init-{}'.format(geom_signature))

    d = {
        'rad': parameters['geometry']['R'],
        'h': parameters['material']['ell'] / parameters['geometry']['n']
    }
    geom_signature = hashlib.md5(str(d).encode('utf-8')).hexdigest()
    fname = os.path.join('../meshes', 'circle-{}'.format(geom_signature))
    if os.path.isfile(fname + '.xml'):
        log(LogLevel.INFO, "Meshfile {} exists".format(fname))
        mesh = dolfin.Mesh("{}.xml".format(fname))
    else:
        log(LogLevel.INFO, "Creating meshfile: %s" % fname)
        log(LogLevel.INFO, "DEBUG: parameters: %s" % d)

        # mesh_template = open('../scripts/templates/circle_template_init.geo')
        if MPI.rank(MPI.comm_world) == 0:

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

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

            with open(fname + ".geo", 'w') as f:
                f.write(geofile)

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

            log(
                LogLevel.INFO,
                'Unable to handle mesh generation at the moment, please generate the mesh and test again.'
            )
            log(LogLevel.INFO, cmd1)
            log(LogLevel.INFO, cmd2)
            sys.exit()
            log(LogLevel.INFO, 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('{}.xml'.format(fname))
        mesh_xdmf = XDMFFile("{}.xdmf".format(fname))
        mesh_xdmf.write(mesh)

    log(LogLevel.INFO, fname)

    # boundary_meshfunction = dolfin.MeshFunction("size_t", mesh, "{}_facet_region.xml".format(fname))
    # cells_meshfunction = dolfin.MeshFunction("size_t", mesh, "{}_physical_region.xml".format(fname))

    # 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
    log(LogLevel.WARNING, 'porcoilclero')

    if size == 1:
        meshf = dolfin.File(os.path.join(outdir, "mesh.xml"))
        meshf << mesh
        plot(mesh)
        plt.savefig(os.path.join(outdir, "mesh.pdf"), bbox_inches='tight')

    log(LogLevel.INFO, 'num vertices {}'.format(mesh.num_vertices()))

    log(
        LogLevel.INFO, 'Number of dofs: {}'.format(
            mesh.num_vertices() * (1 + parameters['general']['dim'])))
    # import pdb; pdb.set_trace()

    with open(os.path.join(outdir, 'parameters.yaml'), "w") as f:
        yaml.dump(parameters, f, default_flow_style=False)

    R = parameters['geometry']['R']
    ell = parameters['material']['ell']

    savelag = 1
    # left = dolfin.CompiledSubDomain("near(x[0], -Lx/2.)", Lx=Lx)
    # right = dolfin.CompiledSubDomain("near(x[0], Lx/2.)", Lx=Lx)
    # 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)

    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")
    u.rename('u', 'u')
    alpha = Function(V_alpha)
    alpha_old = dolfin.Function(alpha.function_space())
    alpha.rename('alpha', 'alpha')
    dalpha = TrialFunction(V_alpha)
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)

    state = {'u': u, 'alpha': 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, dolfin.Constant((0,0)), left),
    # dolfin.DirichletBC(V_u, dolfin.Constant((0,0)), right),
    # dolfin.DirichletBC(V_u, (0, 0), left_bottom_pt, method="pointwise")
    # ]
    bcs_u = [dolfin.DirichletBC(V_u, dolfin.Constant((0., 0.)), 'on_boundary')]
    # bcs_u = []
    # bcs_alpha_l = DirichletBC(V_alpha,  Constant(0.0), left)
    # bcs_alpha_r = DirichletBC(V_alpha, Constant(0.0), right)
    # bcs_alpha =[bcs_alpha_l, bcs_alpha_r]
    # bcs_alpha = [DirichletBC(V_alpha, Constant(0.0), 'on_boundary')]
    bcs_alpha = []
    # bcs_alpha = [DirichletBC(V_alpha, Constant(1.), boundary_meshfunction, 101)]

    bcs = {"damage": bcs_alpha, "elastic": bcs_u}

    ell = parameters['material']['ell']

    # Problem definition
    # Problem definition
    k_res = parameters['material']['k_res']
    a = (1 - alpha)**2. + k_res
    w_1 = parameters['material']['sigma_D0']**2 / parameters['material']['E']
    w = w_1 * alpha
    eps = sym(grad(u))
    eps0t = Expression([['t', 0.], [0., 't']], t=0., degree=0)
    X0 = parameters['geometry']['R']
    lmbda0 = parameters['material']['E'] * parameters['material']['nu'] / (
        1. - parameters['material']['nu'])**2.
    mu0 = parameters['material']['E'] / 2. / (1.0 +
                                              parameters['material']['nu'])
    nu = parameters['material']['nu']
    # Wt = a*parameters['material']['E']*nu/(2*(1-nu**2.)) * tr(eps-eps0t)**2.                                \
    #     + a*parameters['material']['E']*(inner(eps-eps0t, eps-eps0t)/(2.*(1+nu)))                           \
    # + 1./2.*1./parameters['material']['ell_e']**2.*dot(u, u)
    # effective coeff of trace  = lmbda mu / (lmbda + 2 mu) = nu/(1-nu)

    # Wt = a* lmbda0/(lmbda0+ 2.*mu0) * tr(eps-eps0t)**2.                             \
    #     + a*2.*mu0*(inner(eps-eps0t, eps-eps0t))                           \

    Wt = a*parameters['material']['E']*nu/((1+nu)*(1-2*nu)) * tr(eps-eps0t)**2.                             \
        + a*parameters['material']['E']*(inner(eps-eps0t, eps-eps0t)/(2.*(1+nu)))                           \
        + 1./2.*X0**2./parameters['material']['ell_e']**2.*dot(u, u)

    energy = Wt * dx + w_1 * (alpha +
                              (parameters['material']['ell'] / X0**2.) *
                              inner(grad(alpha), grad(alpha))) * dx

    file_out = dolfin.XDMFFile(os.path.join(outdir, "output.xdmf"))
    file_out.parameters["functions_share_mesh"] = True
    file_out.parameters["flush_output"] = True
    file_postproc = dolfin.XDMFFile(os.path.join(outdir, "postprocess.xdmf"))
    file_postproc.parameters["functions_share_mesh"] = True
    file_postproc.parameters["flush_output"] = True
    file_eig = dolfin.XDMFFile(os.path.join(outdir, "perturbations.xdmf"))
    file_eig.parameters["functions_share_mesh"] = True
    file_eig.parameters["flush_output"] = True
    file_bif = dolfin.XDMFFile(os.path.join(outdir, "bifurcation.xdmf"))
    file_bif.parameters["functions_share_mesh"] = True
    file_bif.parameters["flush_output"] = True
    file_bif_postproc = dolfin.XDMFFile(
        os.path.join(outdir, "bifurcation_postproc.xdmf"))
    file_bif_postproc.parameters["functions_share_mesh"] = True
    file_bif_postproc.parameters["flush_output"] = True

    solver = EquilibriumSolver(energy, state, bcs, parameters=parameters)
    stability = StabilitySolver(energy, state, bcs, parameters=parameters)
    # stability = StabilitySolver(energy, state, bcs, parameters = parameters['stability'], rayleigh= [rP, rN])
    linesearch = LineSearch(energy, state)

    load_steps = np.linspace(parameters['loading']['load_min'],
                             parameters['loading']['load_max'],
                             parameters['loading']['n_steps'])

    time_data = []
    time_data_pd = []
    spacetime = []
    lmbda_min_prev = 1e-6
    bifurcated = False
    bifurcation_loads = []
    save_current_bifurcation = False
    bifurc_i = 0
    alpha_bif = dolfin.Function(V_alpha)
    alpha_bif_old = dolfin.Function(V_alpha)
    bifurcation_loads = []
    perturb = False
    _file = dolfin.XDMFFile(os.path.join(outdir, "test.xdmf"))
    log(LogLevel.INFO, '{}'.format(parameters))
    log(
        LogLevel.INFO, 'ell/X0 = {}, ell/ell_e = {}, ell_e/X0 = {}'.format(
            parameters['material']['ell'] / X0,
            parameters['material']['ell'] / parameters['material']['ell_e'],
            parameters['material']['ell_e'] / parameters['geometry']['R']))

    break_after_bifurcation = False
    file_debug = dolfin.XDMFFile('/Users/kumiori/debugalpha.xdmf')
    file_debug.parameters["functions_share_mesh"] = True
    file_debug.parameters["flush_output"] = True

    from matplotlib import cm

    for step, load in enumerate(load_steps):
        log(LogLevel.INFO,
            '==========================================================')
        log(LogLevel.INFO,
            '====================== STEPPING ==========================')
        log(LogLevel.INFO, 'INFO: Solving load t = {:.2f}'.format(load))
        alpha_old.assign(alpha)
        eps0t.t = load
        (time_data_i, am_iter) = solver.solve(debugpath=outdir)

        # Second order stability conditions

        (stable, negev) = stability.solve(solver.damage.problem.lb)
        log(LogLevel.INFO,
            'Current state is{}stable'.format(' ' if stable else ' un'))

        mineig = stability.mineig if hasattr(stability, 'mineig') else 0.0
        log(LogLevel.INFO, 'INFO: lmbda min {}'.format(lmbda_min_prev))
        log(LogLevel.INFO, 'INFO: mineig {}'.format(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
            log(LogLevel.INFO,
                'INFO: About to bifurcate load {} step {}'.format(load, step))
            bifurcation_loads.append(load)
            modes = np.where(stability.eigs < 0)[0]

            bifurc_i += 1

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

        # we postpone the update after the stability check
        if stable:
            solver.update()
            log(
                LogLevel.INFO, '    Current state is{}stable'.format(
                    ' ' if stable else ' un'))
        else:
            # Continuation
            #     save_current_bifurcation = True
            #     save_stability = stability
            iteration = 1
            while stable == False:
                log(LogLevel.CRITICAL,
                    'Continuation iteration {}'.format(iteration))
                #         # linesearch

                cont_data_pre = compile_continuation_data(state, energy)
                perturbation_v = stability.perturbation_v
                perturbation_beta = stability.perturbation_beta
                perturbation_v = stability.perturbations_v[0]
                perturbation_beta = stability.perturbations_beta[0]
                log(LogLevel.INFO, ' Perturbations')
                # log(LogLevel.INFO, '{}'.format(stability.   perturbations_beta))

                pert = [(_v, _b) for _v, _b in zip(
                    stability.perturbations_v, stability.perturbations_beta)]

                for n in range(len(pert)):
                    linesearch.search(
                        {
                            'u': u,
                            'alpha': alpha,
                            'alpha_old': alpha_old
                        }, pert[n][0], pert[n][1])

                if size == 1:
                    fig = plt.figure(
                        figsize=(4, 1.5),
                        dpi=180,
                    )
                    _nmodes = len(pert)
                    for mode in range(_nmodes):
                        plt.subplot(2,
                                    int(_nmodes / 2) + _nmodes % 2, mode + 1)
                        ax = plt.gca()
                        plt.axis('off')
                        plot(stability.perturbations_beta[mode],
                             vmin=-1.,
                             vmax=1.)
                        # ax.set_title('mode: '.format(mode))
                    fig.savefig(os.path.join(outdir,
                                             "modes-{:.3f}.pdf".format(load)),
                                bbox_inches="tight")

                    plt.close()

                    fig = plt.figure(figsize=((_nmodes + 1) * 3, 3),
                                     dpi=80,
                                     facecolor='w',
                                     edgecolor='k')
                    fig.suptitle('Load {:3f}'.format(load), fontsize=16)
                    plt.subplot(2, _nmodes + 1, 1)
                    plt.title('$\\alpha$ (max = {:2.2f})'.format(
                        max(alpha.vector()[:])))
                    plt.set_cmap('coolwarm')
                    plt.axis('off')
                    plot(alpha, vmin=0., vmax=1.)

                    plt.set_cmap('hot')

                    for i, mode in enumerate(pert):
                        plt.subplot(2, _nmodes + 1, i + 2)
                        plt.axis('off')
                        plot(mode[1], cmap=cm.ocean, rowspan=2)

                        h_opt, bounds, energy_perturbations = linesearch.search(
                            {
                                'u': u,
                                'alpha': alpha,
                                'alpha_old': alpha_old
                            }, mode[0], mode[1])
                        # import pdb; pdb.set_trace()
                        # plt.title('mode {}\n$\\lambda_{{{}}}={:.1e},$\n$h_opt$={:.3f}'.format(
                        # i, i, stability.eigs[i], h_opt))
                        # print('plot mode {}'.format(i))
                        # plt.tight_layout(h_pad=0.0, pad=1.5)
                        # plt.savefig(os.path.join(outdir, "modes-{:3.4f}.png".format(load)))

                    for i, mode in enumerate(pert):
                        plt.subplot(2, _nmodes + 1, _nmodes + 2 + 1 + i)
                        plt.axis('off')
                        _pert_beta = mode[1]
                        _pert_v = mode[0]
                        h_opt, bounds, energy_perturbations = linesearch.search(
                            {
                                'u': u,
                                'alpha': alpha,
                                'alpha_old': alpha_old
                            }, mode[0], mode[1])
                        # bounds = mode['interval']
                        # import pdb; pdb.set_trace()
                        if bounds[0] == bounds[1] == 0:
                            plt.plot(bounds[0], 0)
                        else:
                            hs = np.linspace(bounds[0], bounds[1], 100)
                            z = np.polyfit(
                                np.linspace(bounds[0], bounds[1],
                                            len(energy_perturbations)),
                                energy_perturbations,
                                parameters['stability']['order'])
                            p = np.poly1d(z)
                            plt.plot(hs, p(hs), c='k')
                            plt.plot(np.linspace(bounds[0], bounds[1],
                                                 len(energy_perturbations)),
                                     energy_perturbations,
                                     marker='o',
                                     c='k')
                            # plt.axvline(mode['hstar'])
                            plt.axvline(0, lw=.5, c='k')
                        # plt.title('{}'.format(i))
                        plt.tight_layout(h_pad=1.5, pad=1.5)
                    # plt.legend()
                    plt.savefig(
                        os.path.join(outdir, "modes-{:3.4f}.pdf".format(load)))
                    plt.close(fig)
                    plt.clf()
                    log(LogLevel.INFO, 'INFO: plotted modes')

                h_opt, (hmin, hmax), energy_perturbations = linesearch.search(
                    {
                        'u': u,
                        'alpha': alpha,
                        'alpha_old': alpha_old
                    }, perturbation_v, perturbation_beta)
                log(
                    LogLevel.INFO,
                    'h_opt {}, (hmin, hmax) {}, energy_perturbations {}'.
                    format(h_opt, (hmin, hmax), energy_perturbations))

                #         # stable = True

                if h_opt != 0:
                    log(LogLevel.INFO, '    Bifurcating')
                    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()

                    file_debug.write_checkpoint(alpha,
                                                'alpha_new',
                                                0,
                                                append=True)
                    alpha.rename('alpha_new', 'alpha_new')
                    file_debug.write(alpha)
                    __v = alpha - alpha_old
                    _v = dolfin.project(__v, V_alpha)
                    file_debug.write(_v)
                    alpha.rename('alpha', 'alpha')

                    plt.clf()
                    plt.colorbar(dolfin.plot(__v, cmap=cm.binary))
                    plt.savefig(
                        '/Users/kumiori/apert-aold-{}-new-{}.pdf'.format(
                            step, iteration))

                    # import pdb; pdb.set_trace()

                    (time_data_i, am_iter) = solver.solve(debugpath=outdir)

                    file_debug.write_checkpoint(alpha,
                                                'alpha_new_post',
                                                0,
                                                append=True)
                    alpha.rename('alpha_new_post', 'alpha_new_post')
                    file_debug.write(alpha)
                    alpha.rename('alpha', 'alpha')

                    (stable, negev) = stability.solve(solver.damage.problem.lb)
                    log(
                        LogLevel.INFO,
                        '    Continuation iteration {}, current state is{}stable'
                        .format(iteration, ' ' if stable else ' un'))
                    iteration += 1
                    cont_data_post = compile_continuation_data(state, energy)

                    criterion = (cont_data_post['energy'] -
                                 cont_data_pre['energy']
                                 ) / cont_data_pre['energy'] < parameters[
                                     'stability']['cont_rtol']
                    log(LogLevel.INFO,
                        'INFO: Continuation criterion {}'.format(criterion))
                else:
                    # warn
                    log(LogLevel.WARNING,
                        'Found zero increment, we are stuck in the matrix')
                    log(LogLevel.WARNING, 'Continuing load program')
                    import pdb
                    pdb.set_trace()
                    break

            # solver.update()

            # if save_current_bifurcation:
            #     import pdb; pdb.set_trace()
            #     time_data_i['h_opt'] = h_opt
            #     time_data_i['max_h'] = hmax
            #     time_data_i['min_h'] = hmin

            #     modes = np.where(save_stability.eigs < 0)[0]
            #     leneigs = len(modes)
            #     maxmodes = min(3, leneigs)

            #     with file_bif as file:
            #         for n in range(maxmodes):
            #             mode = dolfin.project(save_stability.linsearch[n]['beta_n'], V_alpha)
            #             modename = 'beta-%d'%n
            #             mode.rename(modename, modename)
            #             log(LogLevel.INFO, 'Saved mode {}'.format(modename))
            #             file.write(mode, step)

            #     with file_bif_postproc as file:
            #         # leneigs = len(modes)
            #         # maxmodes = min(3, leneigs)
            #         beta0v = dolfin.project(save_stability.perturbation_beta, V_alpha)
            #         log(LogLevel.DEBUG, 'DEBUG: irrev {}'.format(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-{}'.format(step)), energy_perturbations, allow_pickle=True, fix_imports=True)

            #     if size == 1:
            #         dolfin.plot(perturbation_beta)
            #         plt.savefig(os.path.join(outdir, "pert_beta.pdf"))

            #     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')
            #         file.write(_v, load)
            #         file.write(_beta, load)

            #         break_after_bifurcation = True

        time_data_i["load"] = load
        time_data_i["alpha_max"] = max(alpha.vector()[:])
        time_data_i["elastic_energy"] = dolfin.assemble(a * Wt * dx)
        time_data_i["dissipated_energy"] = dolfin.assemble(
            (w + w_1 * material_parameters['ell']**2. *
             inner(grad(alpha), grad(alpha))) * dx)
        time_data_i["stable"] = stability.stable
        time_data_i["# neg ev"] = stability.negev
        time_data_i["eigs"] = stability.eigs if hasattr(stability,
                                                        'eigs') else np.inf

        log(
            LogLevel.INFO,
            "Load/time step {:.4g}: converged in iterations: {:3d}, err_alpha={:.4e}"
            .format(time_data_i["load"], time_data_i["iterations"][0],
                    time_data_i["alpha_error"][0]))

        time_data.append(time_data_i)
        time_data_pd = pd.DataFrame(time_data)
        # import pdb; pdb.set_trace()

        # plt.figure()
        # plt1 = time_data_pd.plot(
        #     x=time_data_pd["load"],
        #     y=time_data_pd["iterations"],
        #     marker=".",
        #     logy=True,
        #     logx=False,
        # )
        # plt.savefig(os.path.join(outdir, "plot_err.pdf"))

        with file_out as file:
            file.write(alpha, load)
            file.write(u, load)

        with file_postproc as file:
            file.write_checkpoint(alpha,
                                  "alpha-{}".format(step),
                                  step,
                                  append=True)
            file.write_checkpoint(u, "u-{}".format(step), step, append=True)
            log(LogLevel.INFO,
                'INFO: written postprocessing step {}'.format(step))

        if break_after_bifurcation:
            log(LogLevel.CRITICAL, '    Bifurcarted. Breaking here')
            break
            # step = it
            # for s in range(step):
            # log(LogLevel.INFO, 'INFO: reading step {}'.format(s))
            # f.read_checkpoint(alpha, "alpha-{}".format(s))
            # log(LogLevel.INFO, 'INFO: read step {}'.format(step))
            # f.close()
            # import pdb; pdb.set_trace()

        # spacetime.append(get_trace(alpha))

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

    if size == 1:

        def format_space(x, pos, xresol=100):
            return '$%1.1f$' % ((-x + xresol / 2) / xresol)

        def format_time(t, pos, xresol=100):
            return '$%1.1f$' % ((t - parameters['loading']['load_min']) /
                                parameters['loading']['n_steps'] *
                                parameters['loading']['load_max'])

        from matplotlib.ticker import FuncFormatter, MaxNLocator

        # ax = plt.gca()

        # ax.yaxis.set_major_formatter(FuncFormatter(format_space))
        # ax.xaxis.set_major_formatter(FuncFormatter(format_time))

        plot(alpha)
        plt.savefig(os.path.join(outdir, 'alpha.pdf'))
        log(LogLevel.INFO,
            "Saved figure: {}".format(os.path.join(outdir, 'alpha.pdf')))

    return time_data_pd, outdir