Exemplo n.º 1
0
/*
 * This routine is in charge of the Temperature equation.  Virtually all
 * of the terms can be enabled or disabled by parameters read in through the
 * configuration file.  This equation has the form:
 * 
 * dT/dt = div(uT) + w hat z + del^2 T 
 */
void calcTemp()
{
    complex PRECISION * forces = T->force1;
    
    if(tDiff)
    {
        laplacian(T->spectral, forces, 0, 1.0);
    }
    else
    {
        memset(forces, 0, spectralCount * sizeof(complex PRECISION));
    }

    if(tempAdvection)
    {
        //advect the background profile (as long as it is enabled)
        if(tempBackground)
        {
            plusEq(forces, u->vec->z->spectral);
        }

        //advect the perturbations
        p_vector flux = temp1;
        multiply(u->vec->x->spatial, T->spatial, flux->x->spatial);
        multiply(u->vec->y->spatial, T->spatial, flux->y->spatial);
        multiply(u->vec->z->spatial, T->spatial, flux->z->spatial);

        fftForward(flux->x);
        fftForward(flux->y);
        fftForward(flux->z);

        p_field advect = temp2->x;
        divergence(flux, advect);

        minusEq(forces, advect->spectral);

    }
    
    //Apply hyper diffusion to the boundaries
    if(sanitize)
    {
        killBoundaries(T->spatial, forces, 1, 100);
    }
}
Exemplo n.º 2
0
/*
 * This routine is in charge of the momentum equation.  Virtually all
 * of the terms can be enabled or disabled by parameters read in through the
 * configuration file.  This equation has the form:
 * 
 * du/dt = div(alpha BB - uu - P) + (Ra T + B^2 / beta) hat z + Pr del^2 u + F_u 
 * 
 * Since the velocity field is incompressible, the pressure term doesn't matter
 * and is eliminated by taking the curl of the right-hand side.  This is then
 * decomposed into poloidal and toroidal components, which are then used in the
 * time integration.
 */
void calcMomentum()
{
    debug("Calculating momentum forces\n");

    int i,j,k;
    int index;

    if(viscosity)
    {
        //First argument is the field we take the laplacian of.
        //Second argument is where the result is stored.
        //The 0 in the third argument means we overwrite the destination array
        //Pr is the coefficient for this diffusion term.
        laplacian(u->vec->x->spectral, rhs->x->spectral, 0, Pr);
        laplacian(u->vec->y->spectral, rhs->y->spectral, 0, Pr);
        laplacian(u->vec->z->spectral, rhs->z->spectral, 0, Pr);
    }
    else
    {
        //make sure we don't start with garbage
        memset(rhs->x->spectral, 0, spectralCount * sizeof(complex PRECISION));
        memset(rhs->y->spectral, 0, spectralCount * sizeof(complex PRECISION));
        memset(rhs->z->spectral, 0, spectralCount * sizeof(complex PRECISION));
    }
    
    //Apply hyper diffusion to the boundaries
    //This does not currently work and is disabled by default!
    if(sanitize)
    {
        killBoundaries(u->vec->x->spatial, rhs->x->spectral, 1, 100*Pr);
        killBoundaries(u->vec->y->spatial, rhs->y->spectral, 1, 100*Pr);
        killBoundaries(u->vec->z->spatial, rhs->z->spectral, 1, 100*Pr);
    }

    //static forcing is read in from a file and currently only in the u 
    //direction as a function of y and z (to remove nonlinear advection)
    if(momStaticForcing)
    {
        complex PRECISION * xfield = rhs->x->spectral;
        complex PRECISION * ffield = forceField->spectral;
        index = 0;
        for(i = 0; i < spectralCount; i++)
        {
            xfield[i] += ffield[i];
        }
    }

    //
    if(momTimeForcing)
    {
        //Evaluate the force function at the current time.
        fillTimeField(temp1, MOMENTUM);

        plusEq(rhs->x->spectral, temp1->x->spectral);
        plusEq(rhs->y->spectral, temp1->y->spectral);
        plusEq(rhs->z->spectral, temp1->z->spectral);
    }

    
    if(momAdvection)
    {
        p_field tense = temp1->x;

	    //Note here, because I already forgot once.  a 2 as the third
	    //parameter makes things behave as a -= operation!
        multiply(u->vec->x->spatial, u->vec->x->spatial, tense->spatial);
        fftForward(tense);
        partialX(tense->spectral, rhs->x->spectral, 2);

        multiply(u->vec->y->spatial, u->vec->y->spatial, tense->spatial);
        fftForward(tense);
        partialY(tense->spectral, rhs->y->spectral, 2);

        multiply(u->vec->z->spatial, u->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, rhs->z->spectral, 2);

        multiply(u->vec->x->spatial, u->vec->y->spatial, tense->spatial);
        fftForward(tense);
        partialY(tense->spectral, rhs->x->spectral, 2);
        partialX(tense->spectral, rhs->y->spectral, 2);

        multiply(u->vec->x->spatial, u->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, rhs->x->spectral, 2);
        partialX(tense->spectral, rhs->z->spectral, 2);

        multiply(u->vec->y->spatial, u->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, rhs->y->spectral, 2);
        partialY(tense->spectral, rhs->z->spectral, 2);      
    }


    if(lorentz)
    {
        p_field tense = temp1->x;
        p_vector lor = temp2;

        //The third parameter as a 0 means we overwrite the destination array.
        //The third parameter as a 1 means it behaves as a += operation.
        
        multiply(B->vec->x->spatial, B->vec->x->spatial, tense->spatial);
        fftForward(tense);
        partialX(tense->spectral, lor->x->spectral, 0);

        multiply(B->vec->y->spatial, B->vec->y->spatial, tense->spatial);
        fftForward(tense);
        partialY(tense->spectral, lor->y->spectral, 0);

        multiply(B->vec->z->spatial, B->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, lor->z->spectral, 0);

        multiply(B->vec->x->spatial, B->vec->y->spatial, tense->spatial);
        fftForward(tense);
        partialY(tense->spectral, lor->x->spectral, 1);
        partialX(tense->spectral, lor->y->spectral, 1);

        multiply(B->vec->x->spatial, B->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, lor->x->spectral, 1);
        partialX(tense->spectral, lor->z->spectral, 1);

        multiply(B->vec->y->spatial, B->vec->z->spatial, tense->spatial);
        fftForward(tense);
        partialZ(tense->spectral, lor->y->spectral, 1);
        partialY(tense->spectral, lor->z->spectral, 1);

        //TODO: Find a routine to change to include this factor
        complex PRECISION * px = lor->x->spectral;
        complex PRECISION * py = lor->y->spectral;
        complex PRECISION * pz = lor->z->spectral;
        int i;
        for(i = 0; i < spectralCount; i++)
        {
            px[i] *= alpha;
            py[i] *= alpha;
            pz[i] *= alpha;
        }

        plusEq(rhs->x->spectral, px);
        plusEq(rhs->y->spectral, py);
        plusEq(rhs->z->spectral, pz);
    }
    
    if(magBuoy)
    {
        p_field B2 = temp1->x;
        dotProduct(B->vec,B->vec,B2);
        fftForward(B2);
        for(i = 0; i < spectralCount; i++)
        {
            B2->spectral[i] *= magBuoyScale;
        }
        plusEq(rhs->z->spectral, B2->spectral);
    }

    if(buoyancy)
    {
        complex PRECISION * zfield = rhs->z->spectral;
        complex PRECISION * tfield = T->spectral;

        PRECISION factor =  Ra * Pr;
        index = 0;
        for(i = 0; i < spectralCount; i++)
        {
            zfield[i] += factor * tfield[i];
        }
    }
    
    //curl it so we can avoid dealing with the pressure term
    curl(rhs, temp1);

    //The third parameter as a 1 means we store the result in the force
    //arrays for u->sol.
    decomposeCurlSolenoidal(u->sol, temp1, 1);
    debug("Momentum forces done\n");
}
Exemplo n.º 3
0
/*
 * This routine is in charge of the magnetic induction equation.  Virtually all
 * of the terms can be enabled or disabled by parameters read in through the
 * configuration file.  This equation has the form:
 * 
 * dB/dt = curl(u cross B) + Pm/Pr*del^2 B + F_B 
 * 
 * Again we have an incompressible field, so after evaluating the force in 
 * vector notation, we decompose it into poloidal and toroidal scalar fields for
 * the time integration.
 */
void calcMag()
{
    int i,j,k;
    int index;
    debug("Calculating Magnetic forces\n");

    if(magDiff)
    {
        laplacian(B->vec->x->spectral, rhs->x->spectral, 0, Pr/Pm);
        laplacian(B->vec->y->spectral, rhs->y->spectral, 0, Pr/Pm);
        laplacian(B->vec->z->spectral, rhs->z->spectral, 0, Pr/Pm);
    }
    else
    {
        //make sure we don't start with garbage
        memset(rhs->x->spectral, 0, spectralCount * sizeof(complex PRECISION));
        memset(rhs->y->spectral, 0, spectralCount * sizeof(complex PRECISION));
        memset(rhs->z->spectral, 0, spectralCount * sizeof(complex PRECISION));
    }
    
    //Apply hyper diffusion to the boundaries.  Again, this does not currently
    //work!
    if(sanitize)
    {
        killBoundaries(B->vec->x->spatial, rhs->x->spectral, 1, 100*Pr/Pm);
        killBoundaries(B->vec->y->spatial, rhs->y->spectral, 1, 100*Pr/Pm);
        killBoundaries(B->vec->z->spatial, rhs->z->spectral, 1, 100*Pr/Pm);
    }

    //static forcing is currently only in the x direction and a function of y and z
    if(magStaticForcing)
    {
        complex PRECISION * xfield = rhs->x->spectral;
        complex PRECISION * ffield = magForceField->spectral;
        index = 0;
        for(i = 0; i < spectralCount; i++)
        {
            xfield[i] += ffield[i];
        }
    }
    
    //If we are doing the kinematic problem then the velocity field is
    //specified ahead of time.  Note, this conflicts with the momentum equation
    //being enabled.  If both momentum equation and kinematic are enabled, then
    //we will still be doing all the work of both, but right here we will erase
    //any work previously done on the momentum equation, at least insofar as
    //the magnetic field is concerned.
    if(kinematic)
    {
        fillTimeField(u->vec, KINEMATIC);
    }

    //This is really the induction term, not advection, though it does contain
    //advection effects within it.
    if(magAdvect)
    {
        p_vector uxb = temp1;
        p_vector cuxb = temp2;

        crossProduct(u->vec, B->vec, uxb);
        fftForward(uxb->x);
        fftForward(uxb->y);
        fftForward(uxb->z);

        curl(uxb, cuxb);

        plusEq(rhs->x->spectral, cuxb->x->spectral);
        plusEq(rhs->y->spectral, cuxb->y->spectral);
        plusEq(rhs->z->spectral, cuxb->z->spectral);
    }

    if(magTimeForcing)
    {
        fillTimeField(temp1, MAGNETIC);

        plusEq(rhs->x->spectral, temp1->x->spectral);
        plusEq(rhs->y->spectral, temp1->y->spectral);
        plusEq(rhs->z->spectral, temp1->z->spectral);
    }

    //The 1 means we store the result in the force vectors.
    decomposeSolenoidal(B->sol, rhs, 1);
    debug("Magnetic forces done\n");
}
Exemplo n.º 4
0
int main(int argc, char *argv[]) {
    if(argc != 6 && argc != 7) {
        printf("usage: rrg N n s D seed (id_string)\n");
        return 1;
        }
    time_t t1,t2,tI,tF;
    ITensor U,Dg,P,S;
    Index ei;

    // RRG structure parameters
    const int    N  = atoi(argv[1]); // should be n*(power of 2)
    const int    n  = atoi(argv[2]); // initial blocking size
    int          w  = n;             // block size (scales with m)
    int          ll = 0;             // lambda block index
    int          m  = 0;             // RG scale factor

    // AGSP and subspace parameters
    const double t = 0.3;            // Trotter temperature
    const int    M = 100;            // num Trotter steps
    const int    k = 1;              // power of Trotter op (just use 1)
    const int    s = atoi(argv[3]);  // formal s param
    const int    D = atoi(argv[4]);  // formal D param
    
    // computational settings
    const bool   doI = true; // diag restricted Hamiltonian iteratively?

    // setup random sampling
    std::random_device r;
    const int seed = atoi(argv[5]);
    fprintf(stderr,"seed is %d\n",seed);
    std::mt19937 gen(seed);
    std::uniform_real_distribution<double> udist(0.0,1.0);

    FILE *sxfl,*syfl,*szfl,*gsfl;
    char id[128],sxnm[256],synm[256],sznm[256],gsnm[256];
    if(argc == 6) sprintf(id,"rrg-L%d-s%d-D%d",N,s,D);
    else sprintf(id,"%s",argv[6]);
    strcat(sxnm,id); strcat(sxnm,"-sx.dat");
    strcat(synm,id); strcat(synm,"-sy.dat");
    strcat(sznm,id); strcat(sznm,"-sz.dat");
    strcat(gsnm,id); strcat(gsnm,"-gs.dat");
    sxfl = fopen(sxnm,"a");
    syfl = fopen(synm,"a");
    szfl = fopen(sznm,"a");
    gsfl = fopen(gsnm,"a");

    // initialize Hilbert subspaces for each level m = 0,...,log(N/n)
    vector<SpinHalf> hsps;
    for(int x = n ; x <= N ; x *= 2) hsps.push_back(SpinHalf(x));
    SpinHalf hs = hsps.back();
 
    // generate product basis over m=0 Hilbert space
    auto p = int(pow(2,n));
    vector<MPS> V1;
    for(int i = 0 ; i < p ; ++i) {
        InitState istate(hsps[0],"Dn");
        for(int j = 1 ; j <= n ; ++j)
            if(i/(int)pow(2,j-1)%2) istate.set(j,"Up");
        V1.push_back(MPS(istate));
        }
    MPS bSpaceL(hsps[0]);
    MPS bSpaceR(hsps[0]);
    makeVS(V1,bSpaceL,LEFT);
    makeVS(V1,bSpaceR,RIGHT);

    // Hamiltonian parameters
    const double Gamma = 2.0;
    vector<double> J(2*(N-1));
    fprintf(stdout,"# Hamiltonian terms Jx1,Jy1,Jx2,... (seed=%d)\n",seed);
    for(int i = 0 ; i < N-1 ; ++i) {
        J[2*i+0] = pow(udist(gen),Gamma);
        J[2*i+1] = pow(udist(gen),Gamma);
        fprintf(stdout,"%16.14f,%16.14f",J[2*i],J[2*i+1]);
        if(i != N-2) fprintf(stdout,",");
        }
    fprintf(stdout,"\n");
    fflush(stdout);

    // initialize H for full system and extract block Hamiltonians
    AutoMPO autoH(hs);
    std::stringstream sts;
    auto out = std::cout.rdbuf(sts.rdbuf());
    vector<vector<MPO> > Hs(hsps.size());
    for(int i = 1 ; i < N ; ++i) {
        autoH += (J[2*(i-1)]-J[2*(i-1)+1]),"S+",i,"S+",i+1;
        autoH += (J[2*(i-1)]-J[2*(i-1)+1]),"S-",i,"S-",i+1;
        autoH += (J[2*(i-1)]+J[2*(i-1)+1]),"S+",i,"S-",i+1;
        autoH += (J[2*(i-1)]+J[2*(i-1)+1]),"S-",i,"S+",i+1;
        }
    auto H = toMPO<ITensor>(autoH,{"Exact",true});
    std::cout.rdbuf(out);

    for(auto i : args(hsps)) extractBlocks(autoH,Hs[i],hsps[i]);
    
    vector<MPO> prodSz,prodSx,projSzUp,projSzDn,projSxUp,projSxDn;
    for(auto& it : hsps) { 
        auto curSz = sysOp(it,"Sz",2.0).toMPO(); prodSz.push_back(curSz);
        auto curSx = sysOp(it,"Sx",2.0).toMPO(); prodSx.push_back(curSx);
        auto curSzUp = sysOp(it,"Id").toMPO(); curSzUp.plusEq(curSz); curSzUp /= 2.0;
        auto curSzDn = sysOp(it,"Id").toMPO(); curSzDn.plusEq(-1.0*curSz); curSzDn /= 2.0;
        auto curSxUp = sysOp(it,"Id").toMPO(); curSxUp.plusEq(curSx); curSxUp /= 2.0;
        auto curSxDn = sysOp(it,"Id").toMPO(); curSxDn.plusEq(-1.0*curSx); curSxDn /= 2.0;
        projSzUp.push_back(curSzUp); projSzDn.push_back(curSzDn);
        projSxUp.push_back(curSxUp); projSxDn.push_back(curSxDn);
        }   
 
    // approximate the thermal operator exp(-H/t)^k using Trotter
    // and MPO multiplication; temperature of K is k/t
    time(&tI);
    MPO eH(hs);
    twoLocalTrotter(eH,t,M,autoH);
    auto K = eH;    
    for(int i = 1 ; i < k ; ++i) {
        nmultMPO(eH,K,K,{"Cutoff",eps,"Maxm",MAXBD});
        K.Aref(1) *= 1.0/norm(K.A(1));
        }
    
    // INITIALIZATION: reduce dimension by sampling from initial basis, either
    // bSpaceL or bSpaceR depending on how the merge will work
    vector<MPS> Spre;
    for(ll = 0 ; ll < N/n ; ll++) {
        auto xs = ll % 2 ? 1 : n; // location of dangling Select index
        auto cur = ll % 2 ? bSpaceR : bSpaceL;
        Index si("ext",s,Select);
       
        // return orthonormal basis of evecs
        auto eigs = diagHermitian(-overlapT(cur,Hs[0][ll],cur),P,S,{"Maxm",s});
        cur.Aref(xs) *= P*delta(commonIndex(P,S),si);
        regauge(cur,xs,{"Truncate",false});

        Spre.push_back(cur);
        }
    time(&t2);
    fprintf(stderr,"initialization: %.f s\n",difftime(t2,tI));

    // ITERATION: proceed through RRG hierarchy, increasing the scale m
    vector<MPS> Spost;
    for(m = 0 ; (int)Spre.size() > 1 ; ++m,w*=2) {
        fprintf(stderr,"Level %d (w = %d)\n",m,w);
        auto hs = hsps[m];
        auto DD = D;//max(4,D/(int(log2(N/n)-m)));
        auto thr = 1e-8;
        Spost.clear();

        // EXPAND STEP: for each block, expand dimension of subspace with AGSP operators
        for(ll = 0 ; ll < N/w ; ++ll) {
            MPO A(hs) , Hc = Hs[m][ll];
            MPS pre = Spre[ll] , ret(hs);
            int xs = ll % 2 ? 1 : w;

            // STEP 1: extract filtering operators A from AGSP K
            time(&t1);
            restrictMPO(K,A,w*ll+1,DD,ll%2);
            time(&t2);
            fprintf(stderr,"trunc AGSP: %.f s\n",difftime(t2,t1));

            // STEP 2: expand subspace using the mapping A:pre->ret
            time(&t1);
            ret = applyMPO(A,pre,ll%2,{"Cutoff",eps,"Maxm",MAXBD});
            time(&t2);
            fprintf(stderr,"apply AGSP: %.f s\n",difftime(t2,t1));

            // rotate into principal components of subspace, poxsibly reducing dimension
            // and stabilizing numerics, then store subspace in eigenbasis of block H
            time(&t1); 
            diagHermitian(overlapT(ret,ret),U,Dg,{"Cutoff",thr});
            time(&t2);
            ei = Index("ext",int(commonIndex(Dg,U)),Select);
            Dg.apply(invsqrt);
            ret.Aref(xs) *= dag(U)*Dg*delta(prime(commonIndex(Dg,U)),ei);
            fprintf(stderr,"rotate MPS: %.f s\n",difftime(t2,t1));

            auto eigs = diagHermitian(-overlapT(ret,Hs[m][ll],ret),P,S);
            ret.Aref(xs) *= P*delta(commonIndex(P,S),ei);
            ret.Aref(xs) *= 1.0/sqrt(overlapT(ret,ret).real(ei(1),prime(ei)(1)));
            regauge(ret,xs,{"Cutoff",eps});

            fprintf(stderr,"max m: %d\n",maxM(ret));
            Spost.push_back(ret);
            
            }

        // MERGE/REDUCE STEP: construct tensor subspace, sample to reduce dimension
        Spre.clear();
        for(ll = 0 ; ll < N/w ; ll+=2) {
            auto spL = Spost[ll];                // L subspace
            auto spR = Spost[ll+1];              // R subspace

            // STEP 1: find s lowest eigenpairs of restricted H
            time(&t1);
            auto tpH = tensorProdContract(spL,spR,Hs[m+1][ll/2]);
            tensorProdH<ITensor> resH(tpH);
            resH.diag(s,doI);
            P = resH.eigenvectors();
            time(&t2);
            fprintf(stderr,"diag restricted H: %.f s\n",difftime(t2,t1));

            // STEP 2: tensor viable sets on each side and reduce dimension
            MPS ret(hsps[m+1]);
            time(&t1);
            tensorProduct(spL,spR,ret,P,(ll/2)%2);
            time(&t2);
            fprintf(stderr,"tensor product (ll=%d): %.f s\n",ll,difftime(t2,t1));
 
            Spre.push_back(ret);
            }
        }

    // EXIT: extract two lowest energy candidate states to determine gap
    auto res = Spre[0];
    auto fi = Index("ext",s/2,Select);
    vector<MPS> resSz = {res,res};
    
    // project to Sz sectors of the eigenspace
    diagHermitian(overlapT(res,prodSz[m],res),U,Dg);
    resSz[0].Aref(N) *= U*delta(commonIndex(U,Dg),fi);
    diagHermitian(overlapT(res,-1.0*prodSz[m],res),U,Dg);
    resSz[1].Aref(N) *= U*delta(commonIndex(U,Dg),fi);
   
    vector<MPS> evecs(2);
    for(int i : range(2)) {
        auto fc = resSz[i];
        
        // diagonalize H within the Sz sectors
        auto eigs = diagHermitian(-overlapT(fc,H,fc),P,S);
        fc.Aref(N) *= (P*setElt(commonIndex(P,S)(1)));
        
        fc.orthogonalize({"Cutoff",epx,"Maxm",MAXBD});
        fc.normalize();
        if(i == 0)
            fprintf(stderr,"RRG gs energy: %17.14f\n",overlap(fc,H,fc));
        evecs[i] = fc;
        }
    time(&t2);

    Real vz,vx;
    vz = overlap(evecs[0],prodSz[m],evecs[0]); vx = overlap(evecs[0],prodSx[m],evecs[0]);
    fprintf(stderr,"Vz,vx of 0 is: %17.14f,%17.14f\n",vz,vx);
    vz = overlap(evecs[1],prodSz[m],evecs[1]); vx = overlap(evecs[1],prodSx[m],evecs[1]);
    fprintf(stderr,"Vz,vx of 1 is: %17.14f,%17.14f\n",vz,vx);
    int x1_up = (vx > 0.0 ? 1 : 0);

    evecs[0] = exactApplyMPO(evecs[0],projSzUp[m],{"Cutoff",epx});
    evecs[0] = exactApplyMPO(evecs[0],projSxUp[m],{"Cutoff",epx});
    evecs[1] = exactApplyMPO(evecs[1],projSzDn[m],{"Cutoff",epx});
    evecs[1] = exactApplyMPO(evecs[1],(x1_up?projSxUp[m]:projSxDn[m]),{"Cutoff",epx});
    for(auto& it : evecs) it.normalize();

    fprintf(stderr,"gs candidate energy: %17.14f\nRRG BD ",overlap(evecs[0],H,evecs[0]));
    for(const auto& it : evecs) fprintf(stderr,"%d ",maxM(it));
    fprintf(stderr,"\telapsed: %.f s\n",difftime(t2,tI));

    // CLEANUP: use DMRG to improve discovered evecs
    vector<Real> evals(2),e_prev(2);
    int max_iter = 30 , used_max = 0;
    Real flr = 1e-13 , over_conv = 1e-1 , gap = 1.0 , conv = over_conv*gap , max_conv = 1.0;
    for(int i = 0 ; i < (int)evecs.size() ; ++i) evals[i] = overlap(evecs[i],H,evecs[i]);
    for(int i = 0 ; (i < 2 || conv < max_conv) && i < max_iter ; ++i) {
        e_prev = evals;

        time(&t1);
        evals = dmrgMPO(H,evecs,8,{"Penalty",0.1,"Cutoff",epx});
        time(&t2);
        
        gap = evals[1]-evals[0];

        max_conv = 0.0;
        for(auto& j : range(2))
            if(fabs(e_prev[j]-evals[j]) > max_conv) max_conv = e_prev[j]-evals[j];
        
        fprintf(stderr,"DMRG BD ");
        for(const auto& it : evecs) fprintf(stderr,"%3d ",maxM(it));
        fprintf(stderr,"\tgap: %e\tconv=%9.2e,%9.2e\telapsed: %.f s\n",gap,
            e_prev[0]-evals[0],e_prev[1]-evals[1],difftime(t2,t1));
        conv = max(over_conv*gap,flr);
        if(i == max_iter) used_max = 1;
        }

    for(int i = 0 ; i < (int)evecs.size() ; ++i) {
        vz = overlap(evecs[i],prodSz[m],evecs[i]); vx = overlap(evecs[i],prodSx[m],evecs[i]);
        fprintf(stderr,"Vz,vx of %d is: %12.9f,%12.9f\n",i,vz,vx);
        }

    evecs[0] = exactApplyMPO(evecs[0],projSzUp[m],{"Cutoff",1e-16});
    evecs[0] = exactApplyMPO(evecs[0],projSxUp[m],{"Cutoff",1e-16});
    evecs[1] = exactApplyMPO(evecs[1],projSzDn[m],{"Cutoff",1e-16});
    evecs[1] = exactApplyMPO(evecs[1],(x1_up?projSxUp[m]:projSxDn[m]),{"Cutoff",1e-16});
    for(auto& it : evecs) it.normalize();
    for(auto i : range(evecs.size())) evals[i] = overlap(evecs[i],H,evecs[i]);
    time(&tF);

    auto gsR = evecs[0];
    auto ee = measEE(gsR,N/2);
    gap = evals[1]-evals[0];

    fprintf(stderr,"gs: %17.14f gap: %15.9e ee: %10.8f\n",evals[0],gap,ee);
    fprintf(gsfl,"# GS data (L=%d s=%d D=%d seed=%d time=%.f)\n",N,s,D,seed,difftime(tF,tI));
    if(used_max) fprintf(gsfl,"# WARNING max iterations reached\n");
    fprintf(gsfl,"%17.14f\t%15.9e\t%10.8f\n",evals[0],gap,ee);

    // Compute two-point correlation functions in ground state via usual MPS method
    fprintf(sxfl,"# SxSx corr matrix (L=%d s=%d D=%d seed=%d)\n",N,s,D,seed);
    fprintf(syfl,"# SySy corr matrix (L=%d s=%d D=%d seed=%d)\n",N,s,D,seed);
    fprintf(szfl,"# SzSz corr matrix (L=%d s=%d D=%d seed=%d)\n",N,s,D,seed);
    for(int i = 1 ; i <= N ; ++i) {
        gsR.position(i,{"Cutoff",0.0});
        auto SxA = hs.op("Sx",i); auto SyA = hs.op("Sy",i); auto SzA = hs.op("Sz",i);
        for(int j = 1 ; j <= N ; ++j) {
            if(j <= i) {
                fprintf(sxfl,"%15.12f\t",0.0);
                fprintf(syfl,"%15.12f\t",0.0);
                fprintf(szfl,"%15.12f\t",0.0); 
            } else {
                auto SxB = hs.op("Sx",j); auto SyB = hs.op("Sy",j); auto SzB = hs.op("Sz",j);
                fprintf(sxfl,"%15.12f\t",measOp(gsR,SxA,i,SxB,j));
                fprintf(syfl,"%15.12f\t",measOp(gsR,SyA,i,SyB,j));
                fprintf(szfl,"%15.12f\t",measOp(gsR,SzA,i,SzB,j));
                }
            }
        fprintf(sxfl,"\n");
        fprintf(syfl,"\n");
        fprintf(szfl,"\n");
        }

    fclose(sxfl);
    fclose(syfl);
    fclose(szfl);
    fclose(gsfl);

    return 0;
    
    }