Skip to content

Commit bdf0512

Browse files
committed
Merge branch 'devel' of https://github.com/Quantum-Dynamics-Hub/libra-code into devel
2 parents a7841aa + 21bf4e1 commit bdf0512

File tree

4 files changed

+144
-100
lines changed

4 files changed

+144
-100
lines changed

src/dyn/Dynamics.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,8 @@ void compute_dynamics(dyn_variables& dyn_var, bp::dict dyn_params,
13601360
}// AFSSH
13611361
else if(prms.decoherence_algo==3){ ;; } // BCSH of Linjun Wang, nothing to do here
13621362
else if(prms.decoherence_algo==4){ ;; } // MF-SD of Bedard-Hearn, Larsen, Schwartz, nothing to do here
1363-
else if(prms.decoherence_algo==5){ shxf(dyn_var, act_states, old_states); } // SHXF
1363+
//else if(prms.decoherence_algo==5 or prms.decoherence_algo==6){ xf_hop_reset(dyn_var, act_states, old_states); } // SHXF or MQCXF
1364+
else if(prms.decoherence_algo==5){ xf_hop_reset(dyn_var, act_states, old_states); } // SHXF or MQCXF
13641365

13651366
}
13661367
// DISH - the old one

src/dyn/dyn_decoherence.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,11 @@ CMATRIX bcsh(CMATRIX& Coeff, double dt, vector<int>& act_states, MATRIX& reversa
7070
CMATRIX mfsd(MATRIX& p, CMATRIX& Coeff, MATRIX& invM, double dt, vector<MATRIX>& decoherence_rates, nHamiltonian& ham, Random& rnd, int isNBRA);
7171
CMATRIX mfsd(MATRIX& p, CMATRIX& Coeff, MATRIX& invM, double dt, vector<MATRIX>& decoherence_rates, nHamiltonian& ham, Random& rnd);
7272

73-
// For SHXF
74-
void shxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms);
75-
void shxf(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states);
73+
// Independent-trajectory XF methods
74+
void xf_hop_reset(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states);
7675

77-
// For MQCXF
78-
void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms);
76+
void shxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms); // For SHXF
77+
void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms); // For MQCXF
7978

8079
// XF propagation
8180
void update_forces_xf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev);

src/dyn/dyn_decoherence_methods.cpp

Lines changed: 134 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,63 @@ void xf_destroy_AT(dyn_variables& dyn_var, double threshold){
862862
} //traj
863863
}
864864

865+
void xf_destroy_AT(dyn_variables& dyn_var, nHamiltonian& ham, double threshold){
866+
/**
867+
\brief When the electronic state recovers to an adiabatic state, destroy auxiliary trajectories
868+
Here, momentum rescaling is also performed.
869+
*/
870+
int traj;
871+
int ntraj = dyn_var.ntraj;
872+
int nadi = dyn_var.nadi;
873+
874+
double upper_lim = 1.0 - threshold;
875+
double lower_lim = threshold;
876+
877+
for(int traj=0; traj<ntraj; traj++){
878+
vector<int>& is_mixed = dyn_var.is_mixed[traj];
879+
vector<int>& is_first = dyn_var.is_first[traj];
880+
CMATRIX& dm = *dyn_var.dm_adi[traj];
881+
882+
int is_recovered = 0;
883+
884+
for(int i=0; i<nadi; i++){
885+
double a_ii = dm.get(i,i).real();
886+
if(is_mixed[i]==1){
887+
if(a_ii>upper_lim){
888+
is_recovered = 1;
889+
890+
// Before the collapse
891+
vector<int> _id(2, 0); _id[1] = traj;
892+
CMATRIX coeff(nadi, 1);
893+
coeff = dyn_var.ampl_adi->col(traj);
894+
double Epot_old = ham.Ehrenfest_energy_adi(coeff, _id).real();
895+
896+
collapse(*dyn_var.ampl_adi, traj, i, 0);
897+
898+
// After the collapse
899+
coeff = dyn_var.ampl_adi->col(traj);
900+
double Epot = ham.Ehrenfest_energy_adi(coeff, _id).real();
901+
902+
// Rescaling momenta for the energy conservation
903+
MATRIX p_real(dyn_var.ndof, 1); p_real = dyn_var.p->col(traj);
904+
double alpha = compute_kinetic_energy(p_real, *dyn_var.iM) + Epot_old - Epot;
905+
906+
if(alpha > 0.0){alpha /= compute_kinetic_energy(p_real, *dyn_var.iM);}
907+
else{alpha = 0.0;}
908+
909+
for(int idof=0; idof<dyn_var.ndof; idof++){
910+
dyn_var.p->set(idof, traj, dyn_var.p->get(idof, traj) * sqrt(alpha));
911+
}
912+
913+
break;
914+
}
915+
}
916+
} //i
917+
918+
if(is_recovered==1){xf_init_AT(dyn_var, traj, -1);}
919+
} //traj
920+
}
921+
865922
void xf_create_AT(dyn_variables& dyn_var, double threshold){
866923
/**
867924
\brief When the electronic state is in a superposition between adiabatic states, create auxiliary trajectories
@@ -899,6 +956,19 @@ void xf_create_AT(dyn_variables& dyn_var, double threshold){
899956

900957
}
901958

959+
void xf_hop_reset(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states){
960+
int traj;
961+
int ntraj = dyn_var.ntraj;
962+
963+
for(traj = 0; traj < ntraj; traj++){
964+
// When a hop occurs, destroy auxiliary trajectories
965+
if(accepted_states[traj] != initial_states[traj]){
966+
xf_init_AT(dyn_var, traj, -1);
967+
cout << "destroy auxiliary trajectories of traj " << traj << " due to a hop" << endl;
968+
}
969+
}// traj
970+
}
971+
902972
void shxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms){
903973
/**
904974
\brief The generic framework of the SHXF (Surface Hopping based on eXact Factorization) method of
@@ -988,9 +1058,9 @@ void shxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn
9881058
if (alpha < 0.0){ alpha = 0.0;
9891059
if (prms.project_out_aux == 1){
9901060
project_out(*dyn_var.ampl_adi, traj, i);
991-
xf_init_AT(dyn_var, traj, i);
1061+
xf_init_AT(dyn_var, traj, -1);
9921062
cout << "Project out a classically forbidden state " << i << " on traj " << traj <<endl;
993-
continue;
1063+
break;
9941064
}
9951065
}
9961066

@@ -1042,19 +1112,6 @@ void shxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn
10421112

10431113
}
10441114

1045-
void shxf(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states){
1046-
int traj;
1047-
int ntraj = dyn_var.ntraj;
1048-
1049-
for(traj = 0; traj < ntraj; traj++){
1050-
// When a hop occurs, destroy auxiliary trajectories
1051-
if(accepted_states[traj] != initial_states[traj]){
1052-
xf_init_AT(dyn_var, traj, -1);
1053-
cout << "destroy auxiliary trajectories of traj " << traj << " due to a hop" << endl;
1054-
}
1055-
}// traj
1056-
}
1057-
10581115
void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms){
10591116
/**
10601117
\brief The generic framework of the MQCXF (Mixed Quantum-Classical based on eXact Factorization) method of
@@ -1065,7 +1122,7 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
10651122
int nadi = dyn_var.nadi;
10661123
int ndof = dyn_var.ndof;
10671124

1068-
xf_destroy_AT(dyn_var, prms.coherence_threshold);
1125+
xf_destroy_AT(dyn_var, ham, prms.coherence_threshold);
10691126

10701127
xf_create_AT(dyn_var, prms.coherence_threshold);
10711128

@@ -1093,7 +1150,6 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
10931150
} //traj
10941151

10951152
// Propagate auxiliary momenta
1096-
CMATRIX coeff_tmp = *dyn_var.ampl_adi;
10971153
CMATRIX coeff(nadi, 1);
10981154

10991155
for(int traj=0; traj<ntraj; traj++){
@@ -1117,7 +1173,7 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
11171173
}
11181174

11191175
vector<int> _id(2, 0); _id[1] = traj;
1120-
coeff = coeff_tmp.col(traj);
1176+
coeff = dyn_var.ampl_adi->col(traj);
11211177
double Epot = ham.Ehrenfest_energy_adi(coeff, _id).real();
11221178

11231179
double alpha;
@@ -1131,18 +1187,18 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
11311187
else{
11321188
p_aux_temp = p_aux_old.row(i).T();
11331189
alpha = compute_kinetic_energy(p_aux_temp, invM) + ham_adi_prev.get(i,i).real() - ham_adi.get(i,i).real();
1190+
//alpha = compute_kinetic_energy(p_real, invM) + Epot - ham_adi.get(i,i).real();
11341191
}
11351192

11361193
if (alpha < 0.0){ alpha = 0.0;
11371194
if (prms.project_out_aux == 1){
11381195
project_out(*dyn_var.ampl_adi, traj, i);
1139-
xf_init_AT(dyn_var, traj, i);
1196+
xf_init_AT(dyn_var, traj, -1);
11401197
cout << "Project out a classically forbidden state " << i << " on traj " << traj <<endl;
11411198

11421199
// rescaling velocity
11431200
double Epot_old = Epot;
1144-
coeff_tmp = *dyn_var.ampl_adi;
1145-
coeff = coeff_tmp.col(traj);
1201+
coeff = dyn_var.ampl_adi->col(traj);
11461202
Epot = ham.Ehrenfest_energy_adi(coeff, _id).real();
11471203

11481204
alpha = compute_kinetic_energy(p_real, invM) + Epot_old - Epot;
@@ -1151,7 +1207,7 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
11511207
dyn_var.p->set(idof, traj, dyn_var.p->get(idof, traj) * sqrt(alpha));
11521208
}
11531209

1154-
continue;
1210+
break;
11551211
}
11561212
}
11571213

@@ -1165,21 +1221,31 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
11651221
double temp = 0.0;
11661222
for(int idof=0; idof<ndof; idof++){temp += p_aux_old.get(i, idof)*p_aux.get(i,idof);}
11671223
if(temp<0.0){
1168-
int a; complex<double> max_val;
1169-
coeff.max_col_elt(0, max_val, a);
1224+
double Epot_old = Epot;
1225+
1226+
int a = dyn_var.act_states[traj];
11701227
collapse(*dyn_var.ampl_adi, traj, a, 0);
1171-
xf_init_AT(dyn_var, traj, -1);
1172-
cout << "Collapse to the most probable state " << a << " with " << pow(fabs(max_val), 2) <<
1173-
" at a classical turning point on traj " << traj <<endl;
1228+
1229+
// After the collapse
1230+
coeff = dyn_var.ampl_adi->col(traj);
1231+
double Epot = ham.Ehrenfest_energy_adi(coeff, _id).real();
1232+
1233+
// Rescaling momenta for the energy conservation
1234+
p_real = dyn_var.p->col(traj);
1235+
double alpha = compute_kinetic_energy(p_real, invM) + Epot_old - Epot;
1236+
1237+
if(alpha > 0.0){alpha /= compute_kinetic_energy(p_real, invM);}
1238+
else{
1239+
alpha = 0.0;
1240+
cout << "Energy is drifted due to a classically forbidden hop" << endl;
1241+
}
11741242

1175-
// rescaling velocity
1176-
alpha = compute_kinetic_energy(p_real, invM) + Epot - ham_adi.get(a,a).real();
1177-
if (alpha < 0.0){alpha = 0.0;}
1178-
alpha /= compute_kinetic_energy(p_real, invM);
1179-
for(int idof=0; idof<ndof; idof++){
1243+
for(int idof=0; idof<dyn_var.ndof; idof++){
11801244
dyn_var.p->set(idof, traj, dyn_var.p->get(idof, traj) * sqrt(alpha));
11811245
}
11821246

1247+
xf_init_AT(dyn_var, traj, -1);
1248+
cout << "Collapse to the active state " << a << " at a classical turning point on traj " << traj <<endl;
11831249
break;
11841250
}
11851251
}
@@ -1188,51 +1254,28 @@ void mqcxf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dy
11881254
}//i
11891255
}//traj
11901256

1191-
// Propagate the spatial derivative of phases
1192-
if(prms.use_xf_force==0){
1193-
for(int traj=0; traj<ntraj; traj++){
1194-
vector<int>& is_mixed = dyn_var.is_mixed[traj];
1195-
vector<int>& is_first = dyn_var.is_first[traj];
1196-
MATRIX& p_aux = *dyn_var.p_aux[traj];
1197-
MATRIX& p_aux_old = *dyn_var.p_aux_old[traj];
1198-
MATRIX& nab_phase = *dyn_var.nab_phase[traj];
1199-
1200-
for(int i=0; i<nadi; i++){
1201-
if(is_mixed[i]==1){
1202-
if(is_first[i]==1){
1203-
nab_phase.set(i, -1, 0.0);
1204-
}
1205-
else{
1206-
for(int idof=0; idof<ndof; idof++){
1207-
nab_phase.add(i, idof, p_aux.get(i, idof) - p_aux_old.get(i, idof));
1208-
}//idof
1209-
}
1210-
}
1211-
}//i
1212-
} // traj
1213-
}
1214-
else{
1215-
for(int traj=0; traj<ntraj; traj++){
1216-
vector<int>& is_mixed = dyn_var.is_mixed[traj];
1217-
vector<int>& is_first = dyn_var.is_first[traj];
1218-
MATRIX& nab_phase = *dyn_var.nab_phase[traj];
1219-
1220-
CMATRIX E(nadi, nadi);
1221-
E = ham.children[traj]->get_ham_adi();
1257+
// Propagate the spatial derivative of phases; the E-based approximation is used
1258+
for(int traj=0; traj<ntraj; traj++){
1259+
vector<int>& is_mixed = dyn_var.is_mixed[traj];
1260+
vector<int>& is_first = dyn_var.is_first[traj];
1261+
MATRIX& nab_phase = *dyn_var.nab_phase[traj];
1262+
1263+
CMATRIX E(nadi, nadi);
1264+
E = ham.children[traj]->get_ham_adi();
12221265

1223-
MATRIX p_real(ndof, 1);
1224-
p_real = dyn_var.p->col(traj);
1225-
double Ekin = compute_kinetic_energy(p_real, invM);
1226-
1227-
for(int i=0; i<nadi; i++){
1228-
if(is_mixed[i]==1){
1229-
for(int idof=0; idof<ndof; idof++){
1230-
nab_phase.set(i, idof, -0.5*E.get(i,i).real()*dyn_var.p->get(idof,traj)/Ekin);
1231-
}//idof
1232-
}
1233-
}//i
1234-
} // traj
1235-
}
1266+
MATRIX p_real(ndof, 1);
1267+
p_real = dyn_var.p->col(traj);
1268+
double Ekin = compute_kinetic_energy(p_real, invM);
1269+
1270+
double e = 1.0e-6; // masking for classical turning points
1271+
for(int i=0; i<nadi; i++){
1272+
if(is_mixed[i]==1){
1273+
for(int idof=0; idof<ndof; idof++){
1274+
nab_phase.set(i, idof, -0.5*E.get(i,i).real()*dyn_var.p->get(idof,traj)/(Ekin + e));
1275+
}//idof
1276+
}
1277+
}//i
1278+
} // traj
12361279
}
12371280

12381281
void XF_correction(CMATRIX& Ham, dyn_variables& dyn_var, CMATRIX& C, double wp_width, CMATRIX& T, int traj){
@@ -1248,13 +1291,15 @@ void XF_correction(CMATRIX& Ham, dyn_variables& dyn_var, CMATRIX& C, double wp_w
12481291
RHO = T * C * C.H() * T.H();
12491292

12501293
// Compute quantum momenta
1251-
dyn_var.p_quant->set(-1, traj, 0.0);
1294+
int a = dyn_var.act_states[traj];
12521295

1296+
dyn_var.p_quant->set(-1, traj, 0.0);
12531297
for(int i=0; i<nst; i++){
12541298
if(is_mixed[i]==1){
12551299
for(int idof=0; idof<ndof; idof++){
1256-
dyn_var.p_quant->add(idof, traj, 0.5 / pow(wp_width, 2) * RHO.get(i,i).real()
1257-
*(dyn_var.q->get(idof, traj) - dyn_var.q_aux[traj]->get(i, idof)));
1300+
dyn_var.p_quant->add(idof, traj, 0.5 / pow(wp_width, 2.0) * RHO.get(i,i).real()
1301+
*(dyn_var.q_aux[traj]->get(a, idof) - dyn_var.q_aux[traj]->get(i, idof)));
1302+
// *(dyn_var.q->get(idof, traj) - dyn_var.q_aux[traj]->get(i, idof)));
12581303
}
12591304
}
12601305
}
@@ -1307,8 +1352,6 @@ void update_forces_xf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& h
13071352
MATRIX p_real(ndof, 1);
13081353
p_real = dyn_var.p->col(traj);
13091354
double Ekin = compute_kinetic_energy(p_real, invM);
1310-
1311-
C = Coeff.col(traj);
13121355

13131356
// Compute F for each dof
13141357
for(int idof=0; idof<ndof; idof++){
@@ -1327,7 +1370,7 @@ void update_forces_xf(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& h
13271370
// Original form of the decoherence force
13281371
for(int idof=0; idof<ndof; idof++){
13291372
for(int jdof=0; jdof<ndof; jdof++){
1330-
CMATRIX temp = (F[jdof]*C).H() * (F[idof]*C);
1373+
CMATRIX temp = (F[jdof]*C).H() * (F[idof]*C);
13311374
dyn_var.f_xf->add(idof, traj, -2.0*invM.get(jdof,0)*dyn_var.p_quant->get(jdof, traj)*
13321375
(dyn_var.VP->get(jdof, traj)*dyn_var.VP->get(idof, traj) - temp.get(0,0).real() ) );
13331376
}
@@ -1380,15 +1423,16 @@ void propagate_half_xf(dyn_variables& dyn_var, nHamiltonian& Ham, dyn_control_pa
13801423
//================= Basis re-expansion ===================
13811424
CMATRIX P(nadi, nadi);
13821425
CMATRIX T(*dyn_var.proj_adi[itraj]); T.load_identity();
1383-
CMATRIX T_new(nadi, nadi);
13841426

1385-
P = ham->get_time_overlap_adi(); // U_old.H() * U;
1386-
1387-
// More consistent with the new derivations:
1388-
libmeigen::FullPivLU_inverse(P, T_new);
1389-
T_new = orthogonalized_T( T_new );
1390-
1391-
if(prms.assume_always_consistent){ T_new.identity(); }
1427+
// Don't apply T, since hamiltonian elements were already transformed through the transform_all method
1428+
CMATRIX T_new(nadi, nadi); T_new.load_identity();
1429+
//P = ham->get_time_overlap_adi(); // U_old.H() * U;
1430+
//
1431+
//// More consistent with the new derivations:
1432+
//libmeigen::FullPivLU_inverse(P, T_new);
1433+
//T_new = orthogonalized_T( T_new );
1434+
//
1435+
//if(prms.assume_always_consistent){ T_new.identity(); }
13921436

13931437
// Generate the half-time exponential operator
13941438
CMATRIX Hxf_old(nadi, nadi);

src/dyn/libdyn.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,15 +350,15 @@ void export_dyn_decoherence_objects(){
350350
(MATRIX& p, CMATRIX& Coeff, MATRIX& invM, double dt, vector<MATRIX>& decoherence_rates,
351351
nHamiltonian& ham, Random& rnd) = &mfsd;
352352
def("mfsd", expt_mfsd_v2);
353+
354+
void (*expt_xf_hop_reset)
355+
(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states) = &xf_hop_reset;
356+
def("xf_hop_reset", expt_xf_hop_reset);
353357

354358
void (*expt_shxf_v1)
355359
(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms) = &shxf;
356360
def("shxf", expt_shxf_v1);
357361

358-
void (*expt_shxf_v2)
359-
(dyn_variables& dyn_var, vector<int>& accepted_states, vector<int>& initial_states) = &shxf;
360-
def("shxf", expt_shxf_v2);
361-
362362
void (*expt_mqcxf_v1)
363363
(dyn_variables& dyn_var, nHamiltonian& ham, nHamiltonian& ham_prev, dyn_control_params& prms) = &mqcxf;
364364
def("mqcxf", expt_mqcxf_v1);

0 commit comments

Comments
 (0)