/*****************************************************************************

    TRAVIS - Trajectory Analyzer and Visualizer

    http://www.travis-analyzer.de/

    Copyright (c) 2009-2026 Martin Brehm
                  2012-2017 Martin Thomas
                  2016-2020 Sascha Gehrke

    Please cite:  J. Chem. Phys. 2020, 152 (16), 164105.         (DOI 10.1063/5.0005078 )
                  J. Chem. Inf. Model. 2011, 51 (8), 2007-2023.  (DOI 10.1021/ci200217w )

    This file was written by Martin Brehm.

    ---------------------------------------------------------------------------

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*****************************************************************************/


// This must always be the first include directive
#include "config.h"

#include "hybridsim.h"
#include "globalvar.h"
#include "maintools.h"
#include <time.h>


const char *GetRevisionInfo_hybridsim(unsigned int len) {
	static char buf[256];
	GET_REVISION_INFO( buf, len );
	return buf;
}


const char *GetSourceVersion_hybridsim() {
	static char buf[256];
	GET_SOURCE_VERSION( buf );
	return buf;
}



void CHybridSimEngine::Parse() {

	FILE *a;
	int z, zb, z2, z2b, z3, z3b, z4, z4b, i, o, ti, mtl, ty1, rty1, at1, ty2, rty2, at2, i1, i2;
	double tf, tf2, tf3, tf4, tfmin, tfmax, ljsig, ljsigh, ljeps;
	std::vector<double> bondk;
	std::vector<int> fromoffset, tempia;
	CMolecule *m, *m2;
	CSingleMolecule *sm, *sm2;
	CMolBond *mb;
	bool doharm, dolj, tb, newbasis;
	std::vector<std::vector<int> > iaabonds;
	CxString buf, buf2;
	std::vector<std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > > fa6ljsig;
	std::vector<std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > > > fa6ljeps;
	CAtomGroup ag, ag2;
	int istep, iskip, iuse, istride;
	CTimeStep screents;
	std::vector<std::vector<std::vector<std::vector< CDF*> > > > oa4rdf;
	std::vector<std::vector< CDF*> > oa2bond;
	CDF *pdf;


	ljsig = 0;
	ljsigh = 0;
	ljeps = 0;

	mprintf("\n");
	mprintf( WHITE, "    #############################################\n" );
	mprintf( WHITE, "    ####    Set up Hybrid CP2k Simulation    ####\n" );
	mprintf( WHITE, "    ####    (c) Martin Brehm, 2019 - 2026    ####\n" );
	mprintf( WHITE, "    #############################################\n" );
	mprintf("\n");

	BuildNeighborMatrix();

	mprintf(WHITE,"    *** Pre-screening trajectory for bond lengths and pair distances ***\n\n");

	iskip = AskUnsignedInteger("      Skip how many frames at the beginning of the trajectory: [0] ",0);
	iuse = AskUnsignedInteger("      Use how many frames from that position on? [all] ",0);
	istride = AskUnsignedInteger("      Use every n-th frame out of these: [1] ",1);
	mprintf("\n");

	mprintf("      Preparing bond length histograms...\n");

	oa2bond.resize( g_oaMolecules.GetSize() );
	ti = 0;

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		oa2bond[z].resize( ((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize() );
		for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++) {
			oa2bond[z][z2] = new CDF();
			oa2bond[z][z2]->m_fMinVal = 0;
			oa2bond[z][z2]->m_fMaxVal = 500.0;
			oa2bond[z][z2]->m_iResolution = 500;
			oa2bond[z][z2]->Create();
			oa2bond[z][z2]->SetLabelX("Bond Length / pm");
			oa2bond[z][z2]->SetLabelY("Occurrence");
			ti++;
		}
	}

	mprintf("        Prepared %d histograms.\n",ti);

	mprintf("      Preparing pair distance histograms...\n");

	oa4rdf.resize( g_oaMolecules.GetSize() );
	ti = 0;

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		oa4rdf[z].resize( g_oaMolecules.GetSize() );
		for (z2=z;z2<g_oaMolecules.GetSize();z2++) {
			m2 = (CMolecule*)g_oaMolecules[z2];
			oa4rdf[z][z2].resize( m->m_baAtomIndex.GetSize() );
			for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {
				oa4rdf[z][z2][z3].resize( m2->m_baAtomIndex.GetSize() );
				for (z4=0;z4<m2->m_baAtomIndex.GetSize();z4++) {
					oa4rdf[z][z2][z3][z4] = new CDF();
					oa4rdf[z][z2][z3][z4]->m_fMinVal = 0;
					oa4rdf[z][z2][z3][z4]->m_fMaxVal = 1000.0;
					oa4rdf[z][z2][z3][z4]->m_iResolution = 1000;
					oa4rdf[z][z2][z3][z4]->Create();
					oa4rdf[z][z2][z3][z4]->SetLabelX("Pair Distance / pm");
					oa4rdf[z][z2][z3][z4]->SetLabelY("Occurrence");
					ti++;
				}
			}
		}
	}

	mprintf("        Prepared %d histograms.\n",ti);
	mprintf("      Done.\n\n");

	if (!OpenInputTrajectory()) {
		eprintf("Error: Could not open input trajectory.\n\n");
		abort();
	}

	if (iskip != 0) {

		mprintf("      Fast-forwarding to frame %d ...\n",iskip);

		mprintf(WHITE,"        [");
		for (z=0;z<iskip;z++) {
			if (fmod(z,iskip/50.0) < 1.0)
				mprintf(WHITE,"#");
			if (!screents.SkipTimestep(g_fPos)) {
				eprintf("\n      Error: Unexpected end of position trajectory.\n");
				abort();
			}
		}
		mprintf(WHITE,"]\n");
	}

	istep = 0;

	while (true) {

		if ((istep == 0) && (iskip == 0))
			mprintf("\n      Frame %6d ",iskip+istep*istride+1);
		else if ((istep % 50) == 0)
			mprintf("\n      Frame %6d ",iskip+istep*istride);

		mprintf(".");

		for (z=0;z<(istride-1);z++) {
			if (!screents.SkipTimestep(g_fPos)) {
				if (InputTrajectoryEOF()) {
					mprintf("\n      End of trajectory file reached.\n");
					goto _endscreen;
				} else {
					eprintf("\n      Error while skipping trajectory frame.\n");
					abort();
				}
			}
		}

		if (!screents.ReadTimestep(g_fPos,false)) {
			if (InputTrajectoryEOF()) {
				mprintf("\n      End of trajectory file reached.\n");
				goto _endscreen;
			} else {
				eprintf("\n      Error while reading trajectory frame.\n");
				abort();
			}
		}

		istep++;

		screents.CalcCenters();

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++) {

				for (z3=0;z3<m->m_laSingleMolIndex.GetSize();z3++) {

					sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z3]];
					//mb = (CMolBond*)sm->m_oaBonds[z2];
					mb = (CMolBond*)((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds[z2];

	/*				if ((z==2) && (z2 == 0)) {
						CxDVector3 vec1, vec2;
						vec1 = screents.m_vaCoords[ ((CxIntArray*)sm->m_oaAtomOffset[g_waAtomElement[mb->m_iAtomOffset[1]]])->GetAt(g_waAtomMolNumber[mb->m_iAtomOffset[1]]) ];
						vec2 = screents.m_vaCoords[ ((CxIntArray*)sm->m_oaAtomOffset[g_waAtomElement[mb->m_iAtomOffset[0]]])->GetAt(g_waAtomMolNumber[mb->m_iAtomOffset[0]]) ];
						mprintf(" ( %.1f | %.1f | %.1f )  -  ( %.1f | %.1f | %.1f )  =  ( %.1f | %.1f | %.1f )    --> %f\n",
							vec1[0],
							vec1[1],
							vec1[2],
							vec2[0],
							vec2[1],
							vec2[2],
							FoldVector(vec1-vec2)[0],
							FoldVector(vec1-vec2)[1],
							FoldVector(vec1-vec2)[2],
							FoldedLength(vec1-vec2)
						);
					}*/

					oa2bond[z][z2]->AddToBin(
						FoldedLength(
							  screents.m_vaCoords[ ((CxIntArray*)sm->m_oaAtomOffset[g_waAtomElement[mb->m_iAtomOffset[1]]])->GetAt(g_waAtomMolNumber[mb->m_iAtomOffset[1]]) ]
							- screents.m_vaCoords[ ((CxIntArray*)sm->m_oaAtomOffset[g_waAtomElement[mb->m_iAtomOffset[0]]])->GetAt(g_waAtomMolNumber[mb->m_iAtomOffset[0]]) ]
						)
					);
				}
			}

			for (zb=z;zb<g_oaMolecules.GetSize();zb++) {

				m2 = (CMolecule*)g_oaMolecules[zb];

				for (z2=0;z2<m->m_baAtomIndex.GetSize();z2++) {

					if (m->m_baAtomIndex[z2] == g_iVirtAtomType)
						continue;

					for (z2b=0;z2b<m2->m_baAtomIndex.GetSize();z2b++) {

						if (m2->m_baAtomIndex[z2b] == g_iVirtAtomType)
							continue;

						pdf = oa4rdf[z][zb][z2][z2b];

						for (z3=0;z3<m->m_laSingleMolIndex.GetSize();z3++) {

							sm = (CSingleMolecule*)g_oaSingleMolecules[ m->m_laSingleMolIndex[z3] ];

							for (z3b=0;z3b<m2->m_laSingleMolIndex.GetSize();z3b++) {

							//	if ((z == zb) && (z3 > z3b))
							//		continue;

							//	if ((z == zb) && (z3 == z3b) && (z2 > z2b))
							//		continue;

								sm2 = (CSingleMolecule*)g_oaSingleMolecules[ m2->m_laSingleMolIndex[z3b] ];

								for (z4=0;z4<m->m_waAtomCount[z2];z4++) {

									for (z4b=0;z4b<m2->m_waAtomCount[z2b];z4b++) {

							//			if ((z == zb) && (z2 == z2b) && (z3 == z3b) && (z4 > z4b))
							//				continue;

										i1 = ((CxIntArray*)sm->m_oaAtomOffset[z2])->GetAt(z4);
										i2 = ((CxIntArray*)sm2->m_oaAtomOffset[z2b])->GetAt(z4b);

										if (m_pNeighborMatrix[i2*(unsigned long)g_iGesAtomCount+i1] <= 4)
											continue;

										pdf->AddToBin( FoldedLength( screents.m_vaCoords[ i2 ] - screents.m_vaCoords[ i1 ] ) );
									}
								}
							}
						}
					}
				}
			}
		}

		if ((iuse != 0) && (istep >= iuse/istride))
			break;
	}

_endscreen:

	mprintf("\n\n");
	mprintf("      Screened %d frames.\n",istep);

	if (!CloseInputTrajectory()) {
		eprintf("Error: Could not close input trajectory.\n\n");
		abort();
	}

	mprintf(WHITE,"\n    *** Pre-screening done ***\n\n");

	mprintf(WHITE,"    ########   Bond Length Results   ########\n");

	for (z=0;z<g_oaMolecules.GetSize();z++) {

		m = (CMolecule*)g_oaMolecules[z];

		mprintf(WHITE,"\n      *** Molecule Type %d: %s ***\n\n",z+1,m->m_sName);
		mprintf("            Bond           Avg      Min      Max       SD\n");
		mprintf("        ---------------------------------------------------\n");

		for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++) {
			mb = (CMolBond*)((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds[z2];
			oa2bond[z][z2]->CalcMeanSD();
			oa2bond[z][z2]->NormBinSum(1.0);
			mprintf("        %2s%-3d - %2s%-3d    %5.1f    %5.1f    %5.1f    %5.1f\n",
				((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
				g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
				((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
				g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1,
				oa2bond[z][z2]->m_fMean,
				oa2bond[z][z2]->m_fMinInput,
				oa2bond[z][z2]->m_fMaxInput,
				oa2bond[z][z2]->m_fSD
			);
		}
		mprintf("\n");
	}

	mprintf("\n\n");

	mprintf(WHITE,"    ########   Pair Distance Results   ########\n");

	for (z=0;z<g_oaMolecules.GetSize();z++) {

		m = (CMolecule*)g_oaMolecules[z];

		for (zb=z;zb<g_oaMolecules.GetSize();zb++) {

			m2 = (CMolecule*)g_oaMolecules[zb];

			mprintf(WHITE,"\n      *** Pairs for Combination %d: %s  -->  %d: %s ***\n\n",z+1,m->m_sName,zb+1,m2->m_sName);

			mprintf("            Pair           Min. Dist. / pm\n");
			mprintf("        ------------------------------------\n");

			for (z2=0;z2<m->m_baAtomIndex.GetSize();z2++) {

				if (m->m_baAtomIndex[z2] == g_iVirtAtomType)
					continue;

				for (z2b=0;z2b<m2->m_baAtomIndex.GetSize();z2b++) {

					if (m2->m_baAtomIndex[z2b] == g_iVirtAtomType)
						continue;

					pdf = oa4rdf[z][zb][z2][z2b];

					pdf->CalcMeanSD();
					pdf->NormBinSum(1.0);

					mprintf("        %2s  -->  %-2s",
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z2]])->m_pElement->m_sLabel,
						((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z2b]])->m_pElement->m_sLabel
					);

					if (pdf->m_fBinEntries > 0)
						mprintf("        %9.4f", pdf->m_fMinInput );
					else
						mprintf("         ---" );

					mprintf("\n");
				}
			}

			mprintf("\n");
		}
	}

	mprintf("\n\n");


	doharm = AskYesNo("      Apply harmonic bond potentials to some bonds (y/n)? [yes] ",true);

	if (doharm) {

		iaabonds.resize(g_oaMolecules.GetSize());
		bondk.resize(g_oaMolecules.GetSize());

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			mprintf("\n");

			mprintf(WHITE,"      *** Molecule type %d: %s ***\n\n",z+1,m->m_sName);

			ti = AskRangeInteger("        Use all bonds (1), exclude some bonds (2), or select certain bonds (3)? [1] ",1,3,1);

			switch(ti) {

				case 1:
					iaabonds[z].resize(((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize());
					for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++)
						iaabonds[z][z2] = z2;
					break;

				case 2:
					tempia.clear();
					while (true) {
						mprintf("\n");
_bea1:
						AskString_ND("        Enter first atom of the bond to exclude (e.g. C1): ",&buf);
						if (!ParseAtom(buf,z,ty1,rty1,at1)) {
							eprintf("Error: Invalid input.\n");
							goto _bea1;
						}

_bea2:
						AskString_ND("        Enter second atom of the bond to exclude (e.g. C1): ",&buf);
						if (!ParseAtom(buf,z,ty2,rty2,at2)) {
							eprintf("Error: Invalid input.\n");
							goto _bea2;
						}

						sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]];
						for (z2=0;z2<sm->m_oaBonds.GetSize();z2++) {
							mb = (CMolBond*)sm->m_oaBonds[z2];
							if (((g_waAtomRealElement[mb->m_iAtomOffset[0]] == rty1) && (g_waAtomMolNumber[mb->m_iAtomOffset[0]] == at1) &&
								 (g_waAtomRealElement[mb->m_iAtomOffset[1]] == rty2) && (g_waAtomMolNumber[mb->m_iAtomOffset[1]] == at2)) ||
								((g_waAtomRealElement[mb->m_iAtomOffset[0]] == rty2) && (g_waAtomMolNumber[mb->m_iAtomOffset[0]] == at2) &&
								 (g_waAtomRealElement[mb->m_iAtomOffset[1]] == rty1) && (g_waAtomMolNumber[mb->m_iAtomOffset[1]] == at1))) {
								tempia.push_back(z2);
								goto _befound;
							}
						}
						eprintf("Error: Bond not found.\n");
						goto _bea1;
_befound:;
						if (!AskYesNo("        Exclude another bond (y/n)? [no] ",false))
							break;
					}
					mprintf("\n");
					for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++) {
						for (z3=0;z3<(int)tempia.size();z3++)
							if (tempia[z3] == z2)
								goto _exc;
						iaabonds[z].push_back(z2);
_exc:;
					}
					mprintf("        Added %lu / %d bonds.\n\n",iaabonds[z].size(),((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize());
					break;

				case 3:
					while (true) {
						mprintf("\n");
_ba1:
						AskString_ND("        Enter first atom of the bond (e.g. C1): ",&buf);
						if (!ParseAtom(buf,z,ty1,rty1,at1)) {
							eprintf("Error: Invalid input.\n");
							goto _ba1;
						}

_ba2:
						AskString_ND("        Enter second atom of the bond (e.g. C1): ",&buf);
						if (!ParseAtom(buf,z,ty2,rty2,at2)) {
							eprintf("Error: Invalid input.\n");
							goto _ba2;
						}

						sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]];
						for (z2=0;z2<sm->m_oaBonds.GetSize();z2++) {
							mb = (CMolBond*)sm->m_oaBonds[z2];
							if (((g_waAtomRealElement[mb->m_iAtomOffset[0]] == rty1) && (g_waAtomMolNumber[mb->m_iAtomOffset[0]] == at1) &&
								 (g_waAtomRealElement[mb->m_iAtomOffset[1]] == rty2) && (g_waAtomMolNumber[mb->m_iAtomOffset[1]] == at2)) ||
								((g_waAtomRealElement[mb->m_iAtomOffset[0]] == rty2) && (g_waAtomMolNumber[mb->m_iAtomOffset[0]] == at2) &&
								 (g_waAtomRealElement[mb->m_iAtomOffset[1]] == rty1) && (g_waAtomMolNumber[mb->m_iAtomOffset[1]] == at1))) {
								iaabonds[z].push_back(z2);
								goto _bfound;
							}
						}
						eprintf("Error: Bond not found.\n");
						goto _ba1;
_bfound:;
						if (!AskYesNo("        Add another bond (y/n)? [no] ",false))
							break;
					}
					mprintf("\n");
					break;
			}

			bondk[z] = AskFloat("        Enter bond force constant (in kCal mol^-1 Angstrom^-2): [100.0] ",100.0);
		}


		mprintf("\n\n");
		mprintf(WHITE,"    ########   Bond Energies   ########\n");

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			tb = false;

			for (z2=0;z2<((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds.GetSize();z2++) {

				if (!VectorContains(iaabonds[z],z2))
					continue;

				if (!tb) {
					tb = true;
					mprintf(WHITE,"\n      *** Molecule Type %d: %s ***\n\n",z+1,m->m_sName);
					mprintf("            Bond           Avg      Max (kJ mol^-1)\n");
					mprintf("        ----------------------------------------------\n");
				}

				mb = (CMolBond*)((CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]])->m_oaBonds[z2];

				tf  = bondk[z] * 4.184 * SQR( oa2bond[z][z2]->m_fMinInput - oa2bond[z][z2]->m_fMean ) * 1.0E-4;
				tf2 = bondk[z] * 4.184 * SQR( oa2bond[z][z2]->m_fMaxInput - oa2bond[z][z2]->m_fMean ) * 1.0E-4;

				tf3 = 0;
				for (z3=0;z3<oa2bond[z][z2]->m_iResolution;z3++) {
					tf4 = oa2bond[z][z2]->m_fMinVal+(z3+0.5)*(oa2bond[z][z2]->m_fMaxVal-oa2bond[z][z2]->m_fMinVal)/oa2bond[z][z2]->m_iResolution;
					tf3 += oa2bond[z][z2]->m_pBin[z3] * bondk[z] * 4.184 * SQR( tf4 - oa2bond[z][z2]->m_fMean ) * 1.0E-4;
				}

				mprintf("        %2s%-3d - %2s%-3d    %6.2f    %6.2f\n",
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1,
					tf3,
					MAX(tf,tf2)
				);
			}

			if (tb)
				mprintf("\n");
		}
		mprintf("\n");
	}


	 
	mprintf("\n");
	dolj = AskYesNo("      Apply Lennard-Jones repulsion for pairs of atoms (y/n)? [yes] ",true);

	if (dolj) {

		fa6ljsig.resize( g_oaMolecules.GetSize() );
		fa6ljeps.resize( g_oaMolecules.GetSize() );

		ti = 0;

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			fa6ljsig[z].resize( g_oaMolecules.GetSize() );
			fa6ljeps[z].resize( g_oaMolecules.GetSize() );

			for (zb=0;zb<g_oaMolecules.GetSize();zb++) {

				m2 = (CMolecule*)g_oaMolecules[zb];

				fa6ljsig[z][zb].resize( m->m_baAtomIndex.GetSize() );
				fa6ljeps[z][zb].resize( m->m_baAtomIndex.GetSize() );

				for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {

					fa6ljsig[z][zb][z3].resize( m2->m_baAtomIndex.GetSize() );
					fa6ljeps[z][zb][z3].resize( m2->m_baAtomIndex.GetSize() );

					for (z3b=0;z3b<m2->m_baAtomIndex.GetSize();z3b++) {

						fa6ljsig[z][zb][z3][z3b].resize( m->m_waAtomCount[z3] );
						fa6ljeps[z][zb][z3][z3b].resize( m->m_waAtomCount[z3] );

						for (z4=0;z4<m->m_waAtomCount[z3];z4++) {

							fa6ljsig[z][zb][z3][z3b][z4].resize( m2->m_waAtomCount[z3b] );
							fa6ljeps[z][zb][z3][z3b][z4].resize( m2->m_waAtomCount[z3b] );

							for (z4b=0;z4b<m2->m_waAtomCount[z3b];z4b++) {

								//mprintf("%d / %d / %d / %d / %d / %d\n",z,zb,z3,z3b,z4,z4b);

								fa6ljsig[z][zb][z3][z3b][z4][z4b] = 0;
								fa6ljeps[z][zb][z3][z3b][z4][z4b] = 0;
								ti++;
							}
						}
					}
				}
			}
		}

		mprintf("\n      There are %d pairs in total.\n\n",ti);

		ljsig = AskFloat("      Enter factor for Lennard-Jones sigma: [1.0] ",1.0);
		ljsigh = AskFloat("      Enter factor for Lennard-Jones sigma of hydrogen atoms: [0.5] ",0.5);
		ljeps = AskFloat("      Enter value of Lennard-Jones epsilon (in kJ mol^-1): [0.5] ",0.5);

		ti = 0;

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			for (zb=z;zb<g_oaMolecules.GetSize();zb++) {

				m2 = (CMolecule*)g_oaMolecules[zb];

				mprintf("\n");

				mprintf(WHITE,"      *** Molecule type %d (%s)  <--->  %d (%s) ***\n\n",z+1,m->m_sName,zb+1,m2->m_sName);

				if (!AskYesNo("        Add Lennard-Jones repulsion for this pair (y/n)? [yes] ",true))
					continue;

				if (AskYesNo("        Exclude some pairs of atoms (y/n)? [no] ",false)) {

					mprintf("\n          You can use \"*\" as one of the arguments.\n");

					while (true) {

						mprintf("\n");

						ag.Reset();
						ag2.Reset();

_xa1:
						AskString_ND("          Enter first atom of the pair to exclude (e.g. C1): ",&buf);
						if (!ag.ParseAtoms( m, (const char*)buf )) {
							eprintf("Error: Invalid input.\n");
							goto _xa1;
						}

_xa2:
						AskString_ND("          Enter second atom of the pair to exclude (e.g. C1): ",&buf);
						if (!ag2.ParseAtoms( m2, (const char*)buf )) {
							eprintf("Error: Invalid input.\n");
							goto _xa2;
						}

						for (z3=0;z3<ag.m_baAtomType.GetSize();z3++)
							for (z3b=0;z3b<ag2.m_baAtomType.GetSize();z3b++)
								for (z4=0;z4<((CxIntArray*)ag.m_oaAtoms[z3])->GetSize();z4++)
									for (z4b=0;z4b<((CxIntArray*)ag2.m_oaAtoms[z3b])->GetSize();z4b++)
										fa6ljeps[z][zb][ag.m_baAtomType[z3]][ag2.m_baAtomType[z3b]][((CxIntArray*)ag.m_oaAtoms[z3])->GetAt(z4)][((CxIntArray*)ag2.m_oaAtoms[z3b])->GetAt(z4b)] = -1.0;
	
						if (!AskYesNo("          Add another pair to exclude (y/n)? [no] ",false))
							break;
					}
				}

				for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {

					if (m->m_baAtomIndex[z3] == g_iVirtAtomType)
						continue;

					for (z3b=0;z3b<m2->m_baAtomIndex.GetSize();z3b++) {

						if ((z == zb) && (z3 > z3b))
							continue;

						if (m2->m_baAtomIndex[z3b] == g_iVirtAtomType)
							continue;

						if ((mystricmp(((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,"H") == 0) ||
						    (mystricmp(((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,"D") == 0))
							tf = ljsigh * ((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_fVdWRadius;
						else
							tf = ljsig * ((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_fVdWRadius;

						if ((mystricmp(((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_sLabel,"H") == 0) ||
						    (mystricmp(((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_sLabel,"D") == 0))
							tf += ljsigh * ((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_fVdWRadius;
						else
							tf += ljsig * ((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_fVdWRadius;

						//tf *= ljsig; // Bugfix MB 21.01.2025

						for (z4=0;z4<m->m_waAtomCount[z3];z4++) {

							for (z4b=0;z4b<m2->m_waAtomCount[z3b];z4b++) {

								if ((z == zb) && (z3 == z3b) && (z4 > z4b))
									continue;

								if (fa6ljeps[z][zb][z3][z3b][z4][z4b] < 0) // Explicitly excluded before
									continue;

								fa6ljsig[z][zb][z3][z3b][z4][z4b] = tf;
								fa6ljeps[z][zb][z3][z3b][z4][z4b] = ljeps;

								ti++;
							}
						}
					}
				}
			}
		}

		mprintf("\n      Added %d Lennard-Jones pairs.\n\n",ti);


		mprintf("\n\n");

		mprintf(WHITE,"    ########   Pair Energies   ########\n");

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			for (zb=z;zb<g_oaMolecules.GetSize();zb++) {

				m2 = (CMolecule*)g_oaMolecules[zb];

				tb = false;

				for (z2=0;z2<m->m_baAtomIndex.GetSize();z2++) {

					if (m->m_baAtomIndex[z2] == g_iVirtAtomType)
						continue;

					for (z2b=0;z2b<m2->m_baAtomIndex.GetSize();z2b++) {

						if (m2->m_baAtomIndex[z2b] == g_iVirtAtomType)
							continue;

						pdf = oa4rdf[z][zb][z2][z2b];

						if (pdf->m_fBinEntries == 0)
							continue;

						if (fa6ljeps[z][zb][z2][z2b][0][0] <= 0)
							continue;

						if (!tb) {
							tb = true;
							mprintf(WHITE,"\n      *** Pairs for Combination %d: %s  -->  %d: %s ***\n\n",z+1,m->m_sName,zb+1,m2->m_sName);

							mprintf("            Pair          LJ Sigma    Min. Dist.   Avg. LJE   Min. LJE   Max. LJE\n");
							mprintf("        ----------------------------------------------------------------------------\n");
						}

						tf3 = 0;
						tfmin = 1.0e30;
						tfmax = -1.0e30;
						for (z3=0;z3<pdf->m_iResolution;z3++) {

							if (pdf->m_pBin[z3] == 0)
								continue;

							// Distance
							tf4 = pdf->m_fMinVal+(z3+0.5)*(pdf->m_fMaxVal-pdf->m_fMinVal)/pdf->m_iResolution;

							// Sigma/r
							tf2 = fa6ljsig[z][zb][z2][z2b][0][0] / tf4;

							// LJ Energy
							tf = 4.0 * fa6ljeps[z][zb][z2][z2b][0][0] * (pow(tf2,12) - pow(tf2,6));

							if (tf > tfmax)
								tfmax = tf;
							if (tf < tfmin)
								tfmin = tf;

							tf3 += pdf->m_pBin[z3] * tf;
						}


						mprintf("        %2s  -->  %-2s",
							((CAtom*)g_oaAtoms[m->m_baAtomIndex[z2]])->m_pElement->m_sLabel,
							((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z2b]])->m_pElement->m_sLabel
						);

						mprintf( "        %9.4f  %9.4f  %9.4f  %9.4f", fa6ljsig[z][zb][z2][z2b][0][0], pdf->m_fMinInput, tf3, tfmin );
						if (tfmax >= 5.0)
							mprintf( RED, "  %10.4f", tfmax );
						else
							mprintf( "  %10.4f", tfmax );
						mprintf( "\n" );
					}
				}

				if (tb)
					mprintf("\n");
			}
		}
	}

	mprintf("\n");

	newbasis = AskYesNo("      Use new MOLOPT_UZH basis sets (y) or old MOLOPT basis sets (n)? [no] ",false);

	mprintf("\n");

	ti = AskUnsignedInteger("      Which trajectory frame to use for the coordinates in the CP2k input? [last] ",0);


	if (!OpenInputTrajectory()) {
		eprintf("Error: Could not open input trajectory.\n\n");
		abort();
	}

	istep = 0;

	while (true) {

		if (istep == 0)
			mprintf("\n      Step %6d ",istep+1);
		else if ((istep % 5000) == 0)
			mprintf("\n      Step %6d ",istep);

		if ((istep % 100) == 0)
			mprintf(".");

		if (!screents.ReadTimestep(g_fPos,false)) {
			if (InputTrajectoryEOF()) {
				mprintf("\n      End of trajectory file reached.\n");
				break;
			} else {
				eprintf("\n      Error while reading trajectory frame.\n");
				break;
			}
		}

		istep++;

		if ((ti != 0) && (istep == ti))
			break;
	}

	mprintf("\n\n");
	mprintf("    Using frame %d.\n",istep);

	if (!CloseInputTrajectory()) {
		eprintf("Error: Could not close input trajectory.\n\n");
		abort();
	}

	mprintf("\n");
	mprintf("      Writing CP2k input...\n");

	mtl = 0;
	for (z=0;z<g_oaMolecules.GetSize();z++)
		if ((int)strlen(((CMolecule*)g_oaMolecules[z])->m_sName) > mtl)
			mtl = (int)strlen(((CMolecule*)g_oaMolecules[z])->m_sName);


	a = OpenFileWrite( "hybrid.inp", true );


	mfprintf( a, "&MULTIPLE_FORCE_EVALS\n" );
	mfprintf( a, "  FORCE_EVAL_ORDER 2 3\n" );
	mfprintf( a, "&END MULTIPLE_FORCE_EVALS\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&FORCE_EVAL\n" );
	mfprintf( a, "  METHOD MIXED\n" );
	mfprintf( a, "  &MIXED\n" );
	mfprintf( a, "    NGROUPS 1\n" );
	mfprintf( a, "    MIXING_TYPE GENMIX\n" );
	mfprintf( a, "    &GENERIC\n" );
	mfprintf( a, "      MIXING_FUNCTION X+Y\n" );
	mfprintf( a, "      VARIABLES X Y\n" );
	mfprintf( a, "      ERROR_LIMIT 1.0E-10  #  Avoid warning\n" );
	mfprintf( a, "    &END\n" );
	mfprintf( a, "  &END MIXED\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &SUBSYS\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &CELL\n" );
	mfprintf( a, "      ABC %.4f %.4f %.4f\n", g_fBoxX/100.0, g_fBoxY/100.0, g_fBoxZ/100.0 );
	mfprintf( a, "    &END CELL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &COORD\n" );

	fromoffset.resize(g_iGesAtomCount);
	i = 0;

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		for (z2=0;z2<m->m_laSingleMolIndex.GetSize();z2++) {
			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z2]];
			for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {
				if (sm->m_baAtomIndex[z3] == g_iVirtAtomType)
					continue;
				for (z4=0;z4<((CxIntArray*)sm->m_oaAtomOffset[z3])->GetSize();z4++) {
					o = ((CxIntArray*)sm->m_oaAtomOffset[z3])->GetAt(z4);
					mfprintf(
						a,
						"      %2s%d_%-3d  %16.10f  %16.10f  %16.10f  Mol%d   #   Atom %4d, Mol %2d (%-*s) [%3d] %2s%-3d\n",
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
						z+1,
						z4+1,
						screents.m_vaCoords[o][0]/100.0,
						screents.m_vaCoords[o][1]/100.0,
						screents.m_vaCoords[o][2]/100.0,
						z+1,
						i+1,
						z+1,
						mtl,
						m->m_sName,
						z2+1,
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
						z4+1
					);
			/*		mfprintf(
						a,
						"      %2s%d_%-3d  %16.10f  %16.10f  %16.10f  #  Atom %4d, Mol %2d (%-*s) [%3d] %s%d\n",
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
						z+1,
						z4+1,
						ts->m_vaCoords[o][0]/100.0,
						ts->m_vaCoords[o][1]/100.0,
						ts->m_vaCoords[o][2]/100.0,
						i+1,
						z+1,
						mtl,
						m->m_sName,
						z2+1,
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
						z4+1
					);*/
					fromoffset[o] = i;
					i++;
				}
			}
		}
	}

	mfprintf( a, "    &END COORD\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &TOPOLOGY\n" );
	mfprintf( a, "      &GENERATE\n" );
	mfprintf( a, "        # CREATE_MOLECULES\n" );
	mfprintf( a, "        BONDPARM_FACTOR 0.0  #  No automatic bond detection\n" );
	mfprintf( a, "        BONDLENGTH_MIN 0.0\n" );
	mfprintf( a, "        BONDLENGTH_MAX 0.0\n" );

	if (doharm) {
		for (z=0;z<g_oaMolecules.GetSize();z++) {
			m = (CMolecule*)g_oaMolecules[z];
			for (z2=0;z2<m->m_laSingleMolIndex.GetSize();z2++) {
				sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z2]];
				for (z3=0;z3<sm->m_oaBonds.GetSize();z3++) {
		//			if (!VectorContains(iaabonds[z],z3))
		//				continue;
					mb = (CMolBond*)sm->m_oaBonds[z3];
					mfprintf( a, "        &BOND ADD\n" );
					mfprintf(
						a,
						"          ATOMS %4d %4d  #  Mol %2d (%-*s) [%3d] %2s%-2d - %2s%-2d\n",
						fromoffset[mb->m_iAtomOffset[0]]+1,
						fromoffset[mb->m_iAtomOffset[1]]+1,
						z+1,
						mtl,
						m->m_sName,
						z2+1,
						((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
						g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
						((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
						g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1
					);
					mfprintf( a, "        &END BOND\n" );
				}
			}
		}
	}

	mfprintf( a, "      &END GENERATE\n" );
	mfprintf( a, "    &END TOPOLOGY\n" );
	mfprintf( a, "\n" );

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {
			if (m->m_baAtomIndex[z3] == g_iVirtAtomType)
				continue;
			if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"H")==0)
				ti = 1;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"Li")==0)
				ti = 3;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"B")==0)
				ti = 3;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"C")==0)
				ti = 4;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"N")==0)
				ti = 5;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"O")==0)
				ti = 6;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"F")==0)
				ti = 7;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"P")==0)
				ti = 5;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"S")==0)
				ti = 6;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"Cl")==0)
				ti = 7;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"Br")==0)
				ti = 7;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"He")==0)
				ti = 2;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"Ne")==0)
				ti = 8;
			else if (mystricmp((const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName,"Ar")==0)
				ti = 8;
			else
			{
				eprintf("Atom \"%s\" not known to Hybrid module.\n",(const char*)((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_sName);
				ti = 0;
			}
			for (z4=0;z4<m->m_waAtomCount[z3];z4++) {
				mfprintf(
					a,
					"    &KIND %2s%d_%-3d  #  Mol %2d (%-*s) %2s%-3d\n",
					((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
					z+1,
					z4+1,
					z+1,
					mtl,
					m->m_sName,
					((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
					z4+1
				);
				mfprintf( a, "      ELEMENT %s\n", ((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel );
				if (newbasis)
					mfprintf( a, "      BASIS_SET DZVP-MOLOPT-PBE-GTH-q%d\n", ti );
				else
					mfprintf( a, "      BASIS_SET DZVP-MOLOPT-SR-GTH\n" );
				mfprintf( a, "      POTENTIAL GTH-BLYP-q%d\n", ti );
				mfprintf( a, "    &END KIND\n" );
				mfprintf( a, "\n" );
			}
		}
	}

	mfprintf( a, "  &END SUBSYS\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&END FORCE_EVAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&FORCE_EVAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  METHOD Quickstep\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &DFT\n" );
	if (newbasis)
		mfprintf( a, "    BASIS_SET_FILE_NAME BASIS_MOLOPT_UZH\n" );
	else
		mfprintf( a, "    BASIS_SET_FILE_NAME BASIS_MOLOPT\n" );
	mfprintf( a, "    POTENTIAL_FILE_NAME POTENTIAL\n" );
	mfprintf( a, "    &MGRID\n" );
	mfprintf( a, "      CUTOFF 350\n" );
	mfprintf( a, "      NGRIDS 5\n" );
	mfprintf( a, "      REL_CUTOFF 40\n" );
	mfprintf( a, "    &END MGRID\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &QS\n" );
	mfprintf( a, "      EPS_DEFAULT 1.0E-12\n" );
	mfprintf( a, "    &END QS\n" );
	mfprintf( a, "    &SCF\n" );
	mfprintf( a, "      SCF_GUESS ATOMIC\n" );
	mfprintf( a, "      # SCF_GUESS RESTART\n" );
	mfprintf( a, "      MAX_SCF 15\n" );
	mfprintf( a, "      EPS_SCF 1.0E-6\n" );
	mfprintf( a, "      &OT\n" );
	mfprintf( a, "        MINIMIZER DIIS\n" );
	mfprintf( a, "        PRECONDITIONER FULL_KINETIC\n" );
	mfprintf( a, "      &END\n" );
	mfprintf( a, "      &OUTER_SCF\n" );
	mfprintf( a, "        MAX_SCF 20\n" );
	mfprintf( a, "        EPS_SCF 1.0E-6\n" );
	mfprintf( a, "      &END\n" );
	mfprintf( a, "      &PRINT\n" );
	mfprintf( a, "        &RESTART\n" );
	mfprintf( a, "          &EACH\n" );
	mfprintf( a, "            MD 0\n" );
	mfprintf( a, "          &END EACH\n" );
	mfprintf( a, "        &END RESTART\n" );
	mfprintf( a, "      &END PRINT\n" );
	mfprintf( a, "    &END SCF\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &XC\n" );
	mfprintf( a, "      &XC_FUNCTIONAL BLYP\n" );
	mfprintf( a, "      &END XC_FUNCTIONAL\n" );
	mfprintf( a, "      &XC_GRID\n" );
	mfprintf( a, "        XC_DERIV NN10_SMOOTH\n" );
	mfprintf( a, "        XC_SMOOTH_RHO NN10\n" );
	mfprintf( a, "      &END XC_GRID\n" );
	mfprintf( a, "      &vdW_POTENTIAL\n" );
	mfprintf( a, "         DISPERSION_FUNCTIONAL PAIR_POTENTIAL\n" );
	mfprintf( a, "         &PAIR_POTENTIAL\n" );
	mfprintf( a, "           TYPE DFTD3(BJ)\n" );
	mfprintf( a, "           PARAMETER_FILE_NAME dftd3.dat\n" );
	mfprintf( a, "           D3BJ_SCALING  1.00  0.448486  1.875007  3.610679\n" );
	mfprintf( a, "         &END PAIR_POTENTIAL\n" );
	mfprintf( a, "      &END vdW_POTENTIAL\n" );
	mfprintf( a, "    &END XC\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &END DFT\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&END FORCE_EVAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&FORCE_EVAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  METHOD FIST\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &MM\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &FORCEFIELD\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "      IGNORE_MISSING_CRITICAL_PARAMS .TRUE.\n" );
	mfprintf( a, "      VDW_SCALE14 0.0  #  Ignore 1-4 interactions\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "      &SPLINE\n" );
	mfprintf( a, "        EMAX_SPLINE 1.0\n" );
	if (HalfBox() < 1000)
		mfprintf( a, "        RCUT_NB [angstrom] %.2f\n", HalfBox()/100.0 );
	else
		mfprintf( a, "        RCUT_NB [angstrom] 10.0\n" );
	mfprintf( a, "      &END SPLINE\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "      &CHARGES\n" );

	for (z=0;z<g_iGesAtomCount;z++)
		mfprintf( a, "        0.0\n" );

	mfprintf( a, "      &END CHARGES\n" );
	mfprintf( a, "\n" );

	if (doharm) {
		for (z=0;z<g_oaMolecules.GetSize();z++) {
			m = (CMolecule*)g_oaMolecules[z];
			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[0]];
			for (z3=0;z3<sm->m_oaBonds.GetSize();z3++) {
				if (!VectorContains(iaabonds[z],z3))
					continue;
				mb = (CMolBond*)sm->m_oaBonds[z3];
				mfprintf( a, "      &BOND\n" );
				mfprintf(
					a,
					"        ATOMS %2s%d_%-3d %2s%d_%-3d  #  Mol %2d (%-*s) %2s%-3d - %2s%-3d\n",
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
					z+1,
					g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
					z+1,
					g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1,
					z+1,
					mtl,
					m->m_sName,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1
				);
				mfprintf( a, "        KIND HARMONIC\n" );
/*				mprintf("        Molecule %-*s,  Bond %2s%-3d - %2s%-3d:  Average %8.4f pm,  Std.Dev. %8.4f pm\n",
					mtl,
					m->m_sName,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1,
					oa2bond[z][z3]->m_fMean,
					oa2bond[z][z3]->m_fSD
				);*/
				mfprintf( a, "        R0 [angstrom] %.4f\n", oa2bond[z][z3]->m_fMean/100.0 );
				mfprintf( a, "        K [kcalmol*angstrom^-2] %.2f\n", bondk[z] );
				mfprintf( a, "      &END BOND\n" );
				mfprintf( a, "\n" );
			}
		}
	}


	if (dolj) {
		mfprintf( a, "      &NONBONDED\n" );
		mfprintf( a, "\n" );

		for (z=0;z<g_oaMolecules.GetSize();z++) {

			m = (CMolecule*)g_oaMolecules[z];

			for (zb=0;zb<g_oaMolecules.GetSize();zb++) {

				if (z > zb)
					continue;

				m2 = (CMolecule*)g_oaMolecules[zb];

				for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {

					if (m->m_baAtomIndex[z3] == g_iVirtAtomType)
						continue;

					for (z4=0;z4<m->m_waAtomCount[z3];z4++) {

						for (z3b=0;z3b<m2->m_baAtomIndex.GetSize();z3b++) {

							if ((z == zb) && (z3 > z3b))
								continue;

							if (m2->m_baAtomIndex[z3b] == g_iVirtAtomType)
								continue;

							for (z4b=0;z4b<m2->m_waAtomCount[z3b];z4b++) {

								if ((z == zb) && (z3 == z3b) && (z4 > z4b))
									continue;

								if (fa6ljeps[z][zb][z3][z3b][z4][z4b] <= 0)
									continue;

								mfprintf( a, "        &LENNARD-JONES\n" );
								mfprintf(
									a,
									"          ATOMS %2s%d_%-3d %2s%d_%-3d  #  Mol %2d (%-*s) %2s%-3d - Mol %2d (%-*s) %2s%-3d\n",
									((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
									z+1,
									z4+1,
									((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_sLabel,
									zb+1,
									z4b+1,
									z+1,
									mtl,
									m->m_sName,
									((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
									z4+1,
									zb+1,
									mtl,
									m2->m_sName,
									((CAtom*)g_oaAtoms[m2->m_baAtomIndex[z3b]])->m_pElement->m_sLabel,
									z4b+1
								);
								mfprintf( a, "          EPSILON [kjmol] %.3f\n",  fa6ljeps[z][zb][z3][z3b][z4][z4b] );
								mfprintf( a, "          SIGMA [angstrom] %.4f\n", fa6ljsig[z][zb][z3][z3b][z4][z4b]/100.0 );
								mfprintf( a, "        &END LENNARD-JONES\n" );
								mfprintf( a, "\n" );
							}
						}
					}
				}
			}
		}

		mfprintf( a, "      &END NONBONDED\n" );
	}

	mfprintf( a, "\n" );
	mfprintf( a, "    &END FORCEFIELD\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "    &POISSON\n" );
	mfprintf( a, "      PERIODIC NONE\n" );
	mfprintf( a, "      &EWALD\n" );
	mfprintf( a, "        EWALD_TYPE none\n" );
	mfprintf( a, "      &END EWALD\n" );
	mfprintf( a, "    &END POISSON\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &END MM\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&END FORCE_EVAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&GLOBAL\n" );
	mfprintf( a, "  PROJECT Hybrid\n" );
	mfprintf( a, "  RUN_TYPE MD\n" );
	mfprintf( a, "  PRINT_LEVEL LOW\n" );
	mfprintf( a, "  FFTW_PLAN_TYPE EXHAUSTIVE\n" );
	mfprintf( a, "  FFTW_WISDOM_FILE_NAME wisdom.dat\n" );
	mfprintf( a, "&END GLOBAL\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "&MOTION\n" );
	mfprintf( a, "  &MD\n" );
	mfprintf( a, "    ENSEMBLE NVT\n" );
	mfprintf( a, "    STEPS 2500\n" );
	mfprintf( a, "    TIMESTEP 0.5\n" );
	mfprintf( a, "    &THERMOSTAT\n" );
	mfprintf( a, "      TYPE NOSE\n" );
	mfprintf( a, "   #   REGION MASSIVE\n" );
	mfprintf( a, "      &NOSE\n" );
	mfprintf( a, "        TIMECON 100\n" );
	mfprintf( a, "      &END NOSE\n" );
	mfprintf( a, "    &END THERMOSTAT\n" );
	mfprintf( a, "    TEMPERATURE 350\n" );
	mfprintf( a, "  &END MD\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "  &PRINT\n" );
	mfprintf( a, "    &RESTART\n" );
	mfprintf( a, "      BACKUP_COPIES 0\n" );
	mfprintf( a, "      &EACH\n" );
	mfprintf( a, "        MD 1\n" );
	mfprintf( a, "      &END EACH\n" );
	mfprintf( a, "    &END RESTART\n" );
	mfprintf( a, "    &RESTART_HISTORY\n" );
	mfprintf( a, "      &EACH\n" );
	mfprintf( a, "        MD 0\n" );
	mfprintf( a, "      &END EACH\n" );
	mfprintf( a, "    &END RESTART_HISTORY\n" );
	mfprintf( a, "  &END PRINT\n" );
	mfprintf( a, "&END MOTION\n\n" );
	mfprintf( a, "\n" );
	mfprintf( a, "# &EXT_RESTART\n" );
	mfprintf( a, "#   EXTERNAL_FILE Hybrid-1.restart\n" );
	mfprintf( a, "#   # RESTART_THERMOSTAT .FALSE.\n" );
	mfprintf( a, "# &END\n" );
	mfprintf( a, "\n" );

	fclose(a);

	mprintf("      The CP2k input has been written to \"hybrid.inp\".\n");

	mprintf("\n");
	mprintf( WHITE, "    ####  Finished setting up hybrid CP2k simulation  ####\n" );
	mprintf("\n");
}



void CHybridSimEngine::BuildNeighborMatrix() {

	int z, z2, z3, z4, z5, z2s;
	int o1, o2, o3, o4;
	CMolAtom *ma;
	CMolecule *m;
	CSingleMolecule *sm;


	mprintf("    Creating neighbor matrix (occupies %s of RAM) ...\n",FormatBytes((double)g_iGesAtomCount*g_iGesAtomCount*sizeof(unsigned char)));

	try { m_pNeighborMatrix = new unsigned char[((unsigned long)g_iGesAtomCount)*((unsigned long)g_iGesAtomCount)]; } catch(...) { m_pNeighborMatrix = NULL; }
	if (m_pNeighborMatrix == NULL) NewException((double)sizeof(unsigned char)*g_iGesAtomCount*g_iGesAtomCount,__FILE__,__LINE__,__PRETTY_FUNCTION__);

	for (z2=0;z2<g_iGesAtomCount;z2++)
		for (z3=0;z3<g_iGesAtomCount;z3++)
			if (z2 == z3)
				m_pNeighborMatrix[z2*(unsigned long)g_iGesAtomCount+z3] = 0;
			else
				m_pNeighborMatrix[z2*(unsigned long)g_iGesAtomCount+z3] = 9;

	for (z=0;z<g_oaMolecules.GetSize();z++) {

		m = (CMolecule*)g_oaMolecules[z];

		for (z2s=0;z2s<m->m_laSingleMolIndex.GetSize();z2s++) {

			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z2s]];

			for (z2=0;z2<sm->m_oaMolAtoms.GetSize();z2++) {

				ma = (CMolAtom*)sm->m_oaMolAtoms[z2];
				o1 = ma->m_iOffset;

				for (z3=0;z3<sm->m_oaBonds.GetSize();z3++) {

					if (((CMolBond*)sm->m_oaBonds[z3])->m_iAtomOffset[0] == o1)
						o2 = ((CMolBond*)sm->m_oaBonds[z3])->m_iAtomOffset[1];
					else if (((CMolBond*)sm->m_oaBonds[z3])->m_iAtomOffset[1] == o1)
						o2 = ((CMolBond*)sm->m_oaBonds[z3])->m_iAtomOffset[0];
					else
						continue;

					if (m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o2] > 1)
						m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o2] = 1;

					for (z4=0;z4<sm->m_oaBonds.GetSize();z4++) {

						if (((CMolBond*)sm->m_oaBonds[z4])->m_iAtomOffset[0] == o2)
							o3 = ((CMolBond*)sm->m_oaBonds[z4])->m_iAtomOffset[1];
						else if (((CMolBond*)sm->m_oaBonds[z4])->m_iAtomOffset[1] == o2)
							o3 = ((CMolBond*)sm->m_oaBonds[z4])->m_iAtomOffset[0];
						else
							continue;

						if (o3 == o1)
							continue;

						if (m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o3] > 2)
							m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o3] = 2;

						for (z5=0;z5<sm->m_oaBonds.GetSize();z5++) {

							if (((CMolBond*)sm->m_oaBonds[z5])->m_iAtomOffset[0] == o3)
								o4 = ((CMolBond*)sm->m_oaBonds[z5])->m_iAtomOffset[1];
							else if (((CMolBond*)sm->m_oaBonds[z5])->m_iAtomOffset[1] == o3)
								o4 = ((CMolBond*)sm->m_oaBonds[z5])->m_iAtomOffset[0];
							else
								continue;

							if ((o4 == o1) || (o4 == o2))
								continue;

							if (m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o4] > 3)
								m_pNeighborMatrix[o1*(unsigned long)g_iGesAtomCount+o4] = 3;
						}
					}
				}
			}
		}
	}
	mprintf("    Done.\n\n");
}



// Based on the file format description found on
// ftp://ftp.wwpdb.org/pub/pdb/doc/format_descriptions/Format_v33_A4.pdf
bool CHybridSimEngine::WritePDB( CTimeStep *ts, FILE *f, const char *comment ) {

	int z, z2, z3, z4, i, o;
	CMolecule *m;
	CSingleMolecule *sm;
	time_t now;
	struct tm *tm_now;
	const char *month;
	std::vector<int> fromoffset;
	//CMolBond *mb;
	

	if (f == NULL)
		return false;

	now = time(NULL);
	tm_now = localtime(&now);

	switch(tm_now->tm_mon) {
		case  0: month = "JAN"; break;
		case  1: month = "FEB"; break;
		case  2: month = "MAR"; break;
		case  3: month = "APR"; break;
		case  4: month = "MAY"; break;
		case  5: month = "JUN"; break;
		case  6: month = "JUL"; break;
		case  7: month = "AUG"; break;
		case  8: month = "SEP"; break;
		case  9: month = "OCT"; break;
		case 10: month = "NOV"; break;
		case 11: month = "DEC"; break;
		default: month = "???";
	}

	mfprintf(
		f,
		"HEADER    %-40s%02d-%3s-%02d   1TRA\n",
		"TRAVIS Frame",
		tm_now->tm_mday,
		month,
		(tm_now->tm_year+1900)%100
	);

	if (comment == NULL)
		mfprintf( f, "TITLE     (no comment)\n" );
	else if (comment[0] == 0)
		mfprintf( f, "TITLE     (no comment)\n" );
	else
		mfprintf( f, "TITLE     %s\n", comment );

	mfprintf( f, "COMPND    UNNAMED\n" );

	mfprintf( f, "AUTHOR    GENERATED BY TRAVIS HybridSim\n" );

	fromoffset.resize(g_iGesAtomCount);
	i = 0;

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		for (z2=0;z2<m->m_laSingleMolIndex.GetSize();z2++) {
			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z2]];
			for (z3=0;z3<m->m_baAtomIndex.GetSize();z3++) {
				if (sm->m_baAtomIndex[z3] == g_iVirtAtomType)
					continue;
				for (z4=0;z4<((CxIntArray*)sm->m_oaAtomOffset[z3])->GetSize();z4++) {
					o = ((CxIntArray*)sm->m_oaAtomOffset[z3])->GetAt(z4);

					mfprintf(
						f,
						"HETATM%5d %3s  LIG  %4d    %8.3f%8.3f%8.3f%6.2f%6.2f          %2s  \n",
						z+1,
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel,
						1,
						ts->m_vaCoords[o][0]/100.0,
						ts->m_vaCoords[o][1]/100.0,
						ts->m_vaCoords[o][2]/100.0,
						1.0,
						0.0,
						((CAtom*)g_oaAtoms[m->m_baAtomIndex[z3]])->m_pElement->m_sLabel
					);

					fromoffset[o] = i;
					i++;
				}
			}
		}
	}

	for (z=0;z<g_oaMolecules.GetSize();z++) {
		m = (CMolecule*)g_oaMolecules[z];
		for (z2=0;z2<m->m_laSingleMolIndex.GetSize();z2++) {
			sm = (CSingleMolecule*)g_oaSingleMolecules[m->m_laSingleMolIndex[z2]];
			for (z3=0;z3<sm->m_oaBonds.GetSize();z3++) {
				//mb = (CMolBond*)sm->m_oaBonds[z3];
/*				mfprintf( a, "        &BOND ADD\n" );
				mfprintf(
					a,
					"          ATOMS %4d %4d  #  Mol %2d (%-*s) [%3d] %2s%-2d - %2s%-2d\n",
					fromoffset[mb->m_iAtomOffset[0]]+1,
					fromoffset[mb->m_iAtomOffset[1]]+1,
					z+1,
					mtl,
					m->m_sName,
					z2+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[0]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[0]]+1,
					((CAtom*)g_oaAtoms[g_waAtomRealElement[mb->m_iAtomOffset[1]]])->m_pElement->m_sLabel,
					g_waAtomMolNumber[mb->m_iAtomOffset[1]]+1
				);
				mfprintf( a, "        &END BOND\n" );*/
			}
		}
	}


	mfprintf( f, "END\n" );

	return true;
}






