1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2014 The Regents of the University of California
4 //
5 // This file is part of Qbox
6 //
7 // Qbox is distributed under the terms of the GNU General Public License
8 // as published by the Free Software Foundation, either version 2 of
9 // the License, or (at your option) any later version.
10 // See the file COPYING in the root directory of this distribution
11 // or <http://www.gnu.org/licenses/>.
12 //
13 ////////////////////////////////////////////////////////////////////////////////
14 //
15 // ElectricEnthalpy.cpp
16 //
17 ////////////////////////////////////////////////////////////////////////////////
18 
19 #include <iostream>
20 #include <iomanip>
21 #include <complex>
22 #include <cassert>
23 #include <cmath>
24 #include <algorithm> // fill
25 
26 #include "Timer.h"
27 #include "Context.h"
28 #include "Matrix.h"
29 #include "Sample.h"
30 #include "D3vector.h"
31 #include "ElectricEnthalpy.h"
32 #include "MLWFTransform.h"
33 #include "Wavefunction.h"
34 #include "D3vector.h"
35 #include "Basis.h"
36 #include "SlaterDet.h"
37 #include "ChargeDensity.h"
38 #include "FourierTransform.h"
39 #include "UnitCell.h"
40 using namespace std;
41 
42 ///////////////////////////////////////////////////////////////////////////////
vsst(double x) const43 double ElectricEnthalpy::vsst(double x) const
44 {
45   // smooth sawtooth periodic potential function
46   // x in [-1/2, 1/2]
47   // The slope of vsst is 1 at x=0
48   //
49   // The function vsst approximates the identity function x->x
50   // in the interval [-1/2+xcut, 1/2-xcut]
51   const double xcut = 0.05;
52   const double xcut2 = xcut*xcut;
53   // The function vsst is well represented by a
54   // discrete Fourier transform of length np = 2*ng
55   // Some aliasing error will arise if np < 2*ng
56   // The error is determined by the product xcut*ng
57   const int ng = 32;
58   double v = 0.0;
59   for ( int ig = 1; ig < ng; ig++ )
60   {
61     const double g = 2 * M_PI * ig;
62     const double arg = -0.25 * g * g * xcut2;
63     // next line: factor sgn to shift origin by 0.5
64     const int sgn = 1 - 2*(ig%2);
65     const double c = -2.0 * sgn * exp ( arg ) / g;
66     v += c * sin(x*g);
67   }
68   return v;
69 }
70 
71 ///////////////////////////////////////////////////////////////////////////////
ElectricEnthalpy(Sample & s)72 ElectricEnthalpy::ElectricEnthalpy(Sample& s): s_(s), wf_(s.wf),
73   sd_(*(s.wf.sd(0,0))), ctxt_(s.wf.sd(0,0)->context()),
74   basis_(s.wf.sd(0,0)->basis())
75 {
76   onpe0_ = MPIdata::onpe0();
77   e_field_ = s.ctrl.e_field;
78   finite_field_ = norm2(e_field_) != 0.0;
79   compute_quadrupole_ = false;
80 
81   if ( s.ctrl.polarization == "OFF" )
82     pol_type_ = off;
83   else if ( s.ctrl.polarization == "BERRY" )
84     pol_type_ = berry;
85   else if ( s.ctrl.polarization == "MLWF" )
86     pol_type_ = mlwf;
87   else if ( s.ctrl.polarization == "MLWF_REF" )
88     pol_type_ = mlwf_ref;
89   else if ( s.ctrl.polarization == "MLWF_REF_Q" )
90   {
91     pol_type_ = mlwf_ref;
92     compute_quadrupole_ = true;
93   }
94   else
95   {
96     cerr << "ElectricEnthalpy: invalid polarization option" << endl;
97     ctxt_.abort(1);
98   }
99 
100   // do not allocate further objects if polarization is off
101   if ( pol_type_ == off ) return;
102 
103   assert(wf_.nkp()==1);
104   assert(wf_.nspin()==1);
105 
106   dwf_ = new Wavefunction(s.wf);
107   mlwft_ = new MLWFTransform(sd_);
108   mlwft_->set_tol(1.e-10);
109 
110   smat_[0] = smat_[1] = smat_[2] = 0;
111   rwf_[0] = rwf_[1] = rwf_[2] = 0;
112   int nst = sd_.nst();
113 
114   if ( pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
115   {
116     // allocate real space wf arrays for MLWF refinement
117     for ( int idir = 0; idir < 3; idir++ )
118       rwf_[idir] = new Wavefunction(wf_);
119     correction_.resize(nst);
120   }
121   else if ( pol_type_ == berry )
122   {
123     // allocate complex Berry phase matrix
124     int n = sd_.c().n();
125     int nb = sd_.c().nb();
126     for ( int idir = 0; idir < 3; idir++ )
127       smat_[idir] = new ComplexMatrix(ctxt_,n,n,nb,nb);
128   }
129 
130   if ( (pol_type_ != off) && onpe0_ )
131   {
132     cout.setf(ios::fixed,ios::floatfield);
133     cout.setf(ios::right,ios::adjustfield);
134     cout.precision(8);
135     cout << "<e_field> " << e_field_ << " </e_field>" << endl;
136   }
137 
138   mlwfc_.resize(nst);
139   mlwfs_.resize(nst);
140   quad_.resize(nst);
141 
142   tmap["mlwf_update"].reset();
143   tmap["mlwf_trans"].reset();
144   tmap["correction"].reset();
145   tmap["ft"].reset();
146   tmap["real"].reset();
147   tmap["ft"].reset();
148   tmap["dsum"].reset();
149   tmap["real"].reset();
150 }
151 
152 ///////////////////////////////////////////////////////////////////////////////
~ElectricEnthalpy(void)153 ElectricEnthalpy::~ElectricEnthalpy(void)
154 {
155   if ( pol_type_ == off ) return;
156 
157   delete dwf_;
158   delete mlwft_;
159   for ( int idir = 0; idir < 3; idir++ )
160   {
161     delete rwf_[idir];
162     delete smat_[idir];
163   }
164 
165   for ( TimerMap::iterator i = tmap.begin(); i != tmap.end(); i++ )
166   {
167     double time = i->second.real();
168     double tmin, tmax;
169     MPI_Reduce(&time,&tmin,1,MPI_DOUBLE,MPI_MIN,0,MPIdata::comm());
170     MPI_Reduce(&time,&tmax,1,MPI_DOUBLE,MPI_MAX,0,MPIdata::comm());
171     if ( MPIdata::onpe0() && (tmax > 0.0) )
172     {
173       string s = "name=\"" + (*i).first + "\"";
174       cout << "<timing " << left << setw(22) << s
175            << " min=\"" << setprecision(3) << tmin << "\""
176            << " max=\"" << setprecision(3) << tmax << "\"/>"
177            << endl;
178     }
179   }
180 }
181 
182 ///////////////////////////////////////////////////////////////////////////////
update(void)183 void ElectricEnthalpy::update(void)
184 {
185   if ( pol_type_ == off ) return;
186 
187   const UnitCell& cell = sd_.basis().cell();
188   // compute cos and sin matrices
189   tmap["mlwf_update"].start();
190   mlwft_->update();
191   tmap["mlwf_update"].stop();
192   vector<SlaterDet*> sdcos(3), sdsin(3);
193   sdcos[0] = mlwft_->sdcosx();
194   sdcos[1] = mlwft_->sdcosy();
195   sdcos[2] = mlwft_->sdcosz();
196   sdsin[0] = mlwft_->sdsinx();
197   sdsin[1] = mlwft_->sdsiny();
198   sdsin[2] = mlwft_->sdsinz();
199 
200   dipole_ion_ = s_.atoms.dipole();
201   dipole_el_ = D3vector(0,0,0);
202 
203   if ( pol_type_ == mlwf || pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
204   {
205     tmap["mlwf_trans"].start();
206     mlwft_->compute_transform();
207     mlwft_->apply_transform(sd_);
208     tmap["mlwf_trans"].stop();
209     for ( int i = 0; i < sd_.nst(); i++ )
210     {
211       mlwfc_[i] = mlwft_->center(i);
212       mlwfs_[i] = mlwft_->spread(i);
213     }
214 
215     if ( pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
216     {
217       tmap["correction"].start();
218       compute_correction();
219       tmap["correction"].stop();
220     }
221 
222     for ( int i = 0; i < sd_.nst(); i++ )
223     {
224       dipole_el_ -= 2.0 * mlwfc_[i];
225       if ( pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
226         dipole_el_ -= 2.0 * correction_[i];
227     }
228 
229     // compute gradient
230     if ( finite_field_ )
231     {
232       dwf_->clear();
233       for ( int idir = 0; idir < 3; idir++ )
234       {
235         if ( e_field_[idir] != 0.0 )
236         {
237           // MLWF part
238           if ( pol_type_ == mlwf )
239           {
240             const double nst = sd_.nst();
241             std::vector<double> adiag_inv_real(nst,0),adiag_inv_imag(nst,0);
242             for ( int ist = 0; ist < nst; ist ++ )
243             {
244               const std::complex<double>
245                 adiag( mlwft_->adiag(idir*2,ist),mlwft_->adiag(idir*2+1,ist) );
246 
247               adiag_inv_real[ist] = real( std::complex<double>(1,0) / adiag );
248               adiag_inv_imag[ist] = imag( std::complex<double>(1,0) / adiag );
249 
250             }
251 
252             DoubleMatrix ccos(sdcos[idir]->c());
253             DoubleMatrix csin(sdsin[idir]->c());
254             DoubleMatrix cp(dwf_->sd(0,0)->c());
255 
256             int nloc = cp.nloc();
257             int mloc = cp.mloc();
258             int ione = 1;
259 
260             const double fac = cell.a_norm(idir)
261                                * e_field_[idir] / ( 2.0 * M_PI );
262 
263             for (int in = 0; in < nloc; in++)
264             {
265               int ist = cp.jglobal(in);
266               double fac1 = adiag_inv_real[ist] * fac;
267               double fac2 = adiag_inv_imag[ist] * fac;
268 
269               double *ptr1 = &cp[in*mloc],
270                      *ptrcos = &ccos[in*mloc],
271                      *ptrsin = &csin[in*mloc];
272 
273               daxpy(&mloc, &fac2, ptrcos, &ione, ptr1, &ione);
274               daxpy(&mloc, &fac1, ptrsin, &ione, ptr1, &ione);
275 
276             }
277           }
278           else if ( pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
279           {
280             // MLWF_REF part: real-space correction
281             DoubleMatrix cc(rwf_[idir]->sd(0,0)->c());
282             DoubleMatrix cp(dwf_->sd(0,0)->c());
283 
284             int size = cc.size();
285             double alpha = e_field_[idir];
286             int ione = 1;
287             daxpy (&size, &alpha, cc.valptr(), &ione, cp.valptr(), &ione);
288           } // if pol_type_
289         } // if e_field_[idir]
290       } // for idir
291     } // if finite_field_
292   }
293   else if ( pol_type_ == berry )
294   {
295     // proxy matrix
296     DoubleMatrix gradp(dwf_->sd(0,0)->c());
297     if ( finite_field_ )
298       dwf_->clear();
299 
300     for ( int idir = 0; idir < 3; idir++ )
301     {
302       const double fac = cell.a_norm(idir)/( 2.0*M_PI );
303       complex<double>* val = smat_[idir]->valptr();
304 
305       const double* re = mlwft_->a(idir*2)->cvalptr();
306       const double* im = mlwft_->a(idir*2+1)->cvalptr();
307       for ( int i = 0; i < smat_[idir]->size(); i++ )
308         val[i] = complex<double>(re[i],im[i]);
309 
310       // compute determinant of S
311       ComplexMatrix& sm = *smat_[idir];
312       const Context& ctxt = sm.context();
313 
314       // perform LU decomposition of S
315       valarray<int> ipiv;
316       sm.lu(ipiv);
317 
318       int n = sm.n();
319       int nb = sm.nb();
320       // note: m == n, mb == nb
321 
322       // compute determinant from diagonal values of U  (stored in diag of S)
323       // get full diagonal of the matrix in array diag
324       valarray<complex<double> > diag(n);
325       for ( int ii = 0; ii < n; ii++ )
326       {
327         int iii = sm.l(ii) * nb + sm.x(ii);
328         int jjj = sm.m(ii) * nb + sm.y(ii);
329         if ( sm.pr(ii) == ctxt.myrow() &&
330              sm.pc(ii) == ctxt.mycol() )
331           diag[ii] = val[iii+sm.mloc()*jjj];
332       }
333       ctxt.dsum(2*n,1,(double*)&diag[0],2*n);
334 
335       // sum the argument of diagonal elements
336       double sumarg = 0.0;
337       for ( int ii = 0; ii < n; ii++ )
338         sumarg += arg(diag[ii]);
339 
340       // compute determinant
341       complex<double> det = 1.0;
342       for ( int ii = 0; ii < n; ii++ )
343         det *= diag[ii];
344 
345       const int sign = sm.signature(ipiv);
346       if ( sign == -1 )
347         sumarg += M_PI;
348 
349       // assume occupation number of 2.0
350       dipole_el_[idir] = - 2.0 * fac * sumarg;
351 
352       if ( finite_field_ )
353       {
354         // compute inverse of smat
355         sm.inverse_from_lu(ipiv);
356 
357         // real and img part of S^{-1}
358         DoubleMatrix s_real(ctxt_,n,n,nb,nb);
359         DoubleMatrix s_img(ctxt_,n,n,nb,nb);
360         DoubleMatrix sp(sm);
361 
362         int size = s_real.size();
363         int ione = 1, itwo = 2;
364 
365         // copy real and imaginary parts of s to s_real and s_img
366         dcopy(&size,sp.valptr(),&itwo,s_real.valptr(),&ione);
367         dcopy(&size,sp.valptr()+1,&itwo,s_img.valptr(),&ione);
368 
369         // proxy Matrix for cosx|psi> and sinx|psi>
370         DoubleMatrix cosp(sdcos[idir]->c());
371         DoubleMatrix sinp(sdsin[idir]->c());
372 
373         // alpha = E_i * L_i / 2pi
374         double alpha = fac * e_field_[idir];
375 
376         gradp.gemm('n','n',alpha,cosp,s_img,1.0);
377         gradp.gemm('n','n',alpha,sinp,s_real,1.0);
378       }
379     } // for idir
380   }
381 
382   dipole_total_ = dipole_ion_ + dipole_el_;
383   cell.fold_in_ws(dipole_ion_);
384   cell.fold_in_ws(dipole_el_);
385   cell.fold_in_ws(dipole_total_);
386 }
387 
388 ///////////////////////////////////////////////////////////////////////////////
enthalpy(Wavefunction & dwf,bool compute_hpsi)389 double ElectricEnthalpy::enthalpy(Wavefunction& dwf, bool compute_hpsi)
390 {
391   // return zero if polarization is off, or field is zero
392   if ( pol_type_ == off || !finite_field_ )
393     return 0.0;
394 
395   enthalpy_ = - e_field_ * dipole_total_;
396   if ( compute_hpsi )
397   {
398     // assert gamma-only and no spin
399     assert(dwf.nkp()==1 && dwf.nspin()==1);
400     dwf.sd(0,0)->c() += dwf_->sd(0,0)->c();
401   }
402   return enthalpy_;
403 }
404 
405 ///////////////////////////////////////////////////////////////////////////////
406 // Correction scheme by M. Stengel and N. Spaldin,
407 // Phys. Rev. B 73, 075121 (2006)
408 // Calculate correction in real space and derivatives of the correction
409 ///////////////////////////////////////////////////////////////////////////////
compute_correction(void)410 void ElectricEnthalpy::compute_correction(void)
411 {
412   int np0v = basis_.np(0);
413   int np1v = basis_.np(1);
414   int np2v = basis_.np(2);
415   const ComplexMatrix& c = sd_.c();
416   DoubleMatrix cp(c);
417 
418   FourierTransform ft(basis_,np0v,np1v,np2v);
419 
420   int np012v = ft.np012();
421   int np012loc = ft.np012loc();
422   int nst = sd_.nst();
423   int nloc = c.nloc();
424   int mloc = c.mloc();
425 
426   // store (x-x0)|psi> in rwfs
427   rwf_[0]->clear();
428   rwf_[1]->clear();
429   rwf_[2]->clear();
430 
431   ComplexMatrix& cx = rwf_[0]->sd(0,0)->c();
432   ComplexMatrix& cy = rwf_[1]->sd(0,0)->c();
433   ComplexMatrix& cz = rwf_[2]->sd(0,0)->c();
434 
435   DoubleMatrix cpx(cx);
436   DoubleMatrix cpy(cy);
437   DoubleMatrix cpz(cz);
438 
439   // calculate refinements
440   // ref is scaled by np012v
441   vector<double> ref(nst*3);
442   if ( compute_quadrupole_ ) ref.resize(nst*9);
443 
444   // cell size;
445   const UnitCell& cell = sd_.basis().cell();
446   const double ax = cell.amat(0);
447   const double ay = cell.amat(4);
448   const double az = cell.amat(8);
449 
450   // half cell size;
451   const double ax2 = ax / 2.0;
452   const double ay2 = ay / 2.0;
453   const double az2 = az / 2.0;
454 
455   // grid size;
456   const double dx = ax / np0v;
457   const double dy = ay / np1v;
458   const double dz = az / np2v;
459 
460   for ( int i = 0; i < nst; i++ )
461     correction_[i] = D3vector(0,0,0);
462 
463   int niter = 1;
464   // check if override from the debug variable
465   // use: set debug mlwf_ref_niter <value>
466   const map<string,string>& debug_map = s_.ctrl.debug;
467 
468   map<string,string>::const_iterator imap =
469     debug_map.find("MLWF_REF_NITER");
470   if ( imap != debug_map.end() )
471   {
472     const string val = imap->second;
473     istringstream is(val);
474     is >> niter;
475     if ( MPIdata::onpe0() )
476       cout << " override mlwf_ref_niter value: niter = " << niter << endl;
477     assert(niter > 0);
478   }
479   for ( int iter = 0; iter < niter; iter++ )
480   {
481     fill(ref.begin(),ref.end(),0.0);
482 
483     // wavefunctions in real space
484     vector<complex<double> > wftmp(np012loc);
485     vector<complex<double> > xwftmp(np012loc);
486     vector<complex<double> > ywftmp(np012loc);
487     vector<complex<double> > zwftmp(np012loc);
488 
489     for ( int in = 0; in < nloc; in++ )
490     {
491       int n = c.jglobal(in);
492       double* pref;
493       if ( compute_quadrupole_ )
494         pref = &ref[9*n];
495       else
496         pref = &ref[3*n];
497 
498       // real space wavefunction in wftmp
499       tmap["ft"].start();
500       ft.backward(c.cvalptr(mloc*in),&wftmp[0]);
501       tmap["ft"].stop();
502 
503       tmap["real"].start();
504       double x0 = mlwfc_[n][0] + correction_[n][0];
505       double y0 = mlwfc_[n][1] + correction_[n][1];
506       double z0 = mlwfc_[n][2] + correction_[n][2];
507 
508       // compute shifted sawtooth potentials vx, vy, vz
509       vector<double> vx(ft.np0()), vy(ft.np1()), vz(ft.np2());
510       for ( int i = 0; i < vx.size(); i++ )
511       {
512         double x = i * dx - x0;
513         if ( x >  ax2 ) x -= ax;
514         if ( x < -ax2 ) x += ax;
515 #ifdef NO_VSST
516         vx[i] = x;
517 #else
518         vx[i] = ax * vsst(x/ax);
519 #endif
520       }
521       for ( int j = 0; j < vy.size(); j++ )
522       {
523         double y = j * dy - y0;
524         if ( y >  ay2 ) y -= ay;
525         if ( y < -ay2 ) y += ay;
526 #ifdef NO_VSST
527         vy[j] = y;
528 #else
529         vy[j] = ay * vsst(y/ay);
530 #endif
531       }
532       for ( int k = 0; k < vz.size(); k++ )
533       {
534         double z = k * dz - z0;
535         if ( z >  az2 ) z -= az;
536         if ( z < -az2 ) z += az;
537 #ifdef NO_VSST
538         vz[k] = z;
539 #else
540         vz[k] = az * vsst(z/az);
541 #endif
542       }
543 
544       for ( int i = 0; i < np012loc; i++ )
545       {
546         int ix = ft.i(i);
547         int iy = ft.j(i);
548         int iz = ft.k(i);
549 
550         const double wft = real(wftmp[i]);
551         const double xwft = wft * vx[ix];
552         const double ywft = wft * vy[iy];
553         const double zwft = wft * vz[iz];
554 
555         pref[0] += wft * xwft;
556         pref[1] += wft * ywft;
557         pref[2] += wft * zwft;
558 
559         xwftmp[i] = xwft;
560         ywftmp[i] = ywft;
561         zwftmp[i] = zwft;
562 
563         if ( compute_quadrupole_ )
564         {
565           pref[3] += xwft * xwft;
566           pref[4] += ywft * ywft;
567           pref[5] += zwft * zwft;
568           pref[6] += xwft * ywft;
569           pref[7] += ywft * zwft;
570           pref[8] += zwft * xwft;
571         }
572       } // for i
573       tmap["real"].stop();
574 
575       // ft to get xwf in reciprocal space at the last iteration
576       if ( iter == niter - 1 )
577       {
578         tmap["ft"].start();
579         if ( e_field_[0] != 0.0 )
580           ft.forward(&xwftmp[0],cx.valptr(mloc*in));
581         if ( e_field_[1] != 0.0 )
582           ft.forward(&ywftmp[0],cy.valptr(mloc*in));
583         if ( e_field_[2] != 0.0 )
584           ft.forward(&zwftmp[0],cz.valptr(mloc*in));
585         tmap["ft"].stop();
586       } // if
587     } //for in
588 
589     ctxt_.barrier();
590     tmap["dsum"].start();
591     if ( compute_quadrupole_ )
592       ctxt_.dsum(9*nst,1,&ref[0],9*nst);
593     else
594       ctxt_.dsum(3*nst,1,&ref[0],3*nst);
595     tmap["dsum"].stop();
596 
597     tmap["real"].start();
598     if ( compute_quadrupole_ )
599     {
600       for ( int ist = 0; ist < nst; ist++ )
601       {
602         D3vector& pcor = correction_[ist];
603         D3tensor& pquad = quad_[ist];
604 
605         pcor[0] += ref[ist*9]/np012v;
606         pcor[1] += ref[ist*9+1]/np012v;
607         pcor[2] += ref[ist*9+2]/np012v;
608         pquad.setdiag ( 0, ref[ist*9+3]/np012v - pcor[0] * pcor[0] );
609         pquad.setdiag ( 1, ref[ist*9+4]/np012v - pcor[1] * pcor[1] );
610         pquad.setdiag ( 2, ref[ist*9+5]/np012v - pcor[2] * pcor[2] );
611         pquad.setoffdiag ( 0, ref[ist*9+6]/np012v - pcor[0] * pcor[1] );
612         pquad.setoffdiag ( 1, ref[ist*9+7]/np012v - pcor[1] * pcor[2] );
613         pquad.setoffdiag ( 2, ref[ist*9+8]/np012v - pcor[2] * pcor[0] );
614       }
615     }
616     else
617     {
618       for ( int ist = 0; ist < nst; ist++ )
619       {
620         D3vector& pcor = correction_[ist];
621         pcor[0] += ref[ist*3]/np012v;
622         pcor[1] += ref[ist*3+1]/np012v;
623         pcor[2] += ref[ist*3+2]/np012v;
624       }
625     }
626     tmap["real"].stop();
627   } // for iter
628 }
629 
630 ////////////////////////////////////////////////////////////////////////////////
print(ostream & os) const631 void ElectricEnthalpy::print(ostream& os) const
632 {
633   if ( pol_type_ == off ) return;
634 
635   os << fixed << right << setprecision(8);
636   // print MLWF centers if pol_type_ == MLWF or MLWF_REF or MLWF_REF_Q
637   if ( pol_type_ == mlwf || pol_type_ == mlwf_ref || pol_type_ == mlwf_ref_q )
638   {
639     int nst = sd_.nst();
640     os << " <mlwf_set size=\"" << nst << "\">" << endl;
641     for ( int i = 0; i < nst; i++ )
642     {
643       os << " <mlwf center=\"" << setprecision(8)
644          << setw(12) << mlwfc_[i].x << " "
645          << setw(12) << mlwfc_[i].y << " "
646          << setw(12) << mlwfc_[i].z << " \"\n"
647          << "       spread=\" " << mlwfs_[i] << " \"/>" << endl;
648       if ( pol_type_ == mlwf_ref )
649       {
650         os << " <mlwf_ref center=\"" << setprecision(8)
651            << setw(12) << mlwfc_[i].x + correction_[i].x << " "
652            << setw(12) << mlwfc_[i].y + correction_[i].y << " "
653            << setw(12) << mlwfc_[i].z + correction_[i].z << " \"";
654         if ( compute_quadrupole_ )
655         {
656           // add spread attribute
657           os << " \n     spread=\" " << sqrt(quad_[i].trace()) << " \"";
658         }
659         os << "/>" << endl;
660 
661         if ( compute_quadrupole_ )
662           os << "    <quad>"
663              << setw(12) << quad_[i][0] << " "
664              << setw(12) << quad_[i][4] << " "
665              << setw(12) << quad_[i][8] << " "
666              << setw(12) << quad_[i][1] << " "
667              << setw(12) << quad_[i][2] << " "
668              << setw(12) << quad_[i][5]
669              << " </quad>" << endl;
670       }
671     }
672     os << " </mlwf_set>" << endl;
673   }
674 
675   // print dipole
676   os << setprecision(10) << fixed << right;
677   os << " <dipole>\n";
678   os << "   <dipole_ion>   "
679      << setw(14) << dipole_ion_.x << " "
680      << setw(14) << dipole_ion_.y << " "
681      << setw(14) << dipole_ion_.z << " </dipole_ion>\n";
682   os << "   <dipole_el>    "
683      << setw(14) << dipole_el_.x << " "
684      << setw(14) << dipole_el_.y << " "
685      << setw(14) << dipole_el_.z << " </dipole_el>\n";
686   os << "   <dipole_total> "
687      << setw(14) << dipole_total_.x << " "
688      << setw(14) << dipole_total_.y << " "
689      << setw(14) << dipole_total_.z << " </dipole_total>\n";
690   os << " </dipole>\n";
691 
692   if ( compute_quadrupole_ )
693   {
694     D3tensor q_mlwfc;
695     D3tensor q_mlwfs;
696     for ( int ist = 0; ist < sd_.nst(); ist++ )
697     {
698       D3vector ctr = mlwfc_[ist];
699       for (int j=0; j<3; j++)
700       {
701         for (int k = 0; k < 3; k++)
702           q_mlwfc[j*3+k] -= 2.0 * ctr[j] * ctr[k];
703       }
704       q_mlwfs -= quad_[ist] * 2.0;
705     } // for ist
706 
707     D3tensor q_ion = s_.atoms.quadrupole();
708     D3tensor q_mlwf = q_mlwfc + q_mlwfs;
709     //total primitive quadrupoles
710     D3tensor q_total = q_ion + q_mlwf;
711     //traceless quadrupole
712     D3tensor q_traceless = q_total;
713     q_traceless.traceless();
714 
715     os << " <quadrupole> " << endl;
716     os << "   <quadrupole_ion> " << endl
717        << q_ion
718        << "   </quadrupole_ion>" << endl;
719     os << "   <quadrupole_el> " << endl
720        << q_mlwf
721        << "   </quadrupole_el>" << endl;
722     os << "   <quadrupole_total> " << endl
723        << q_total
724        << "   </quadrupole_total>" << endl;
725     os << "   <traceless_quadrupole> " << endl
726        << q_traceless
727        << "   </traceless_quadrupole>" << endl;
728     char uplo = 'u';
729     D3vector eigval;
730     D3tensor eigvec;
731     q_traceless.syev(uplo, eigval, eigvec);
732     os << "   <traceless_quadrupole_eigval> " << endl
733        << "    " << eigval << endl
734        << "   </traceless_quadrupole_eigval>" << endl;
735     os << "   <traceless_quadrupole_eigvec> " << endl
736        << eigvec
737        << "   </traceless_quadrupole_eigvec>" << endl;
738     os << " </quadrupole> " << endl;
739   }
740 
741 }
742 
743 ////////////////////////////////////////////////////////////////////////////////
set_e_field(D3vector e_field_val)744 void ElectricEnthalpy::set_e_field(D3vector e_field_val)
745 {
746   e_field_ = e_field_val;
747   finite_field_ = norm2(e_field_) != 0.0;
748 }
749 
750 ////////////////////////////////////////////////////////////////////////////////
operator <<(ostream & os,const ElectricEnthalpy & e)751 ostream& operator<< ( ostream& os, const ElectricEnthalpy& e )
752 {
753   e.print(os);
754   return os;
755 }
756