[0b990d] | 1 | //
|
---|
| 2 | // molfreq.cc
|
---|
| 3 | //
|
---|
| 4 | // Copyright (C) 1996 Limit Point Systems, Inc.
|
---|
| 5 | //
|
---|
| 6 | // Author: Curtis Janssen <cljanss@limitpt.com>
|
---|
| 7 | // Maintainer: LPS
|
---|
| 8 | //
|
---|
| 9 | // This file is part of the SC Toolkit.
|
---|
| 10 | //
|
---|
| 11 | // The SC Toolkit is free software; you can redistribute it and/or modify
|
---|
| 12 | // it under the terms of the GNU Library General Public License as published by
|
---|
| 13 | // the Free Software Foundation; either version 2, or (at your option)
|
---|
| 14 | // any later version.
|
---|
| 15 | //
|
---|
| 16 | // The SC Toolkit is distributed in the hope that it will be useful,
|
---|
| 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 19 | // GNU Library General Public License for more details.
|
---|
| 20 | //
|
---|
| 21 | // You should have received a copy of the GNU Library General Public License
|
---|
| 22 | // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
|
---|
| 23 | // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
---|
| 24 | //
|
---|
| 25 | // The U.S. Government is granted a limited license as per AL 91-7.
|
---|
| 26 | //
|
---|
| 27 |
|
---|
| 28 | #ifdef __GNUC__
|
---|
| 29 | #pragma implementation
|
---|
| 30 | #endif
|
---|
| 31 |
|
---|
| 32 | #include <util/misc/math.h>
|
---|
| 33 | #include <util/class/scexception.h>
|
---|
| 34 | #include <util/misc/formio.h>
|
---|
| 35 | #include <util/state/stateio.h>
|
---|
| 36 | #include <util/group/message.h>
|
---|
| 37 | #include <math/symmetry/corrtab.h>
|
---|
| 38 | #include <math/scmat/local.h>
|
---|
| 39 | #include <math/scmat/blocked.h>
|
---|
| 40 | #include <chemistry/molecule/molfreq.h>
|
---|
| 41 | #include <chemistry/molecule/molrender.h>
|
---|
| 42 |
|
---|
| 43 | using namespace std;
|
---|
| 44 | using namespace sc;
|
---|
| 45 |
|
---|
| 46 | #undef DEBUG
|
---|
| 47 |
|
---|
| 48 | static ClassDesc MolecularFrequencies_cd(
|
---|
| 49 | typeid(MolecularFrequencies),"MolecularFrequencies",3,"public SavableState",
|
---|
| 50 | 0, create<MolecularFrequencies>, create<MolecularFrequencies>);
|
---|
| 51 |
|
---|
| 52 | MolecularFrequencies::MolecularFrequencies(const Ref<KeyVal>& keyval)
|
---|
| 53 | {
|
---|
| 54 | mol_ << keyval->describedclassvalue("molecule");
|
---|
| 55 | if (mol_.null()) {
|
---|
| 56 | throw InputError("missing required input of type Molecule",
|
---|
| 57 | __FILE__, __LINE__, "molecule", 0,
|
---|
| 58 | class_desc());
|
---|
| 59 | }
|
---|
| 60 | KeyValValueRefDescribedClass def_pg(mol_->point_group().pointer());
|
---|
| 61 | pg_ << keyval->describedclassvalue("point_group", def_pg);
|
---|
| 62 | nirrep_ = pg_->char_table().nirrep();
|
---|
| 63 | debug_ = keyval->booleanvalue("debug");
|
---|
| 64 | nfreq_ = 0;
|
---|
| 65 | freq_ = 0;
|
---|
| 66 | }
|
---|
| 67 |
|
---|
| 68 | MolecularFrequencies::~MolecularFrequencies()
|
---|
| 69 | {
|
---|
| 70 | delete[] nfreq_;
|
---|
| 71 | if (freq_) {
|
---|
| 72 | for (int i=0; i<nirrep_; i++) {
|
---|
| 73 | delete[] freq_[i];
|
---|
| 74 | }
|
---|
| 75 | delete[] freq_;
|
---|
| 76 | }
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | MolecularFrequencies::MolecularFrequencies(StateIn& si):
|
---|
| 80 | SavableState(si)
|
---|
| 81 | {
|
---|
| 82 | int i;
|
---|
| 83 |
|
---|
| 84 | if (si.version(::class_desc<MolecularFrequencies>()) < 3) {
|
---|
| 85 | throw FileOperationFailed("cannot restore from old version",
|
---|
| 86 | __FILE__, __LINE__, 0,
|
---|
| 87 | FileOperationFailed::Corrupt,
|
---|
| 88 | class_desc());
|
---|
| 89 | }
|
---|
| 90 |
|
---|
| 91 | mol_ << SavableState::restore_state(si);
|
---|
| 92 | pg_ << SavableState::restore_state(si);
|
---|
| 93 |
|
---|
| 94 | si.get(nirrep_);
|
---|
| 95 | si.get(nfreq_);
|
---|
| 96 | for (i=0; i<nirrep_; i++) si.get(freq_[i]);
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | void
|
---|
| 100 | MolecularFrequencies::save_data_state(StateOut& so)
|
---|
| 101 | {
|
---|
| 102 | int i;
|
---|
| 103 |
|
---|
| 104 | SavableState::save_state(mol_.pointer(),so);
|
---|
| 105 | SavableState::save_state(pg_.pointer(),so);
|
---|
| 106 |
|
---|
| 107 | so.put(nirrep_);
|
---|
| 108 | so.put(nfreq_,nirrep_);
|
---|
| 109 | for (i=0; i<nirrep_; i++) so.put(freq_[i],nfreq_[i]);
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 | void
|
---|
| 113 | MolecularFrequencies::compute_frequencies(const RefSymmSCMatrix &xhessian)
|
---|
| 114 | {
|
---|
| 115 | int i, coor;
|
---|
| 116 |
|
---|
| 117 | RefSCMatrix symmbasis
|
---|
| 118 | = MolecularHessian::cartesian_to_symmetry(mol_,pg_);
|
---|
| 119 | BlockedSCMatrix *bsymmbasis = dynamic_cast<BlockedSCMatrix*>(symmbasis.pointer());
|
---|
| 120 |
|
---|
| 121 | kit_ = xhessian->kit();
|
---|
| 122 | d3natom_ = xhessian->dim();
|
---|
| 123 | symkit_ = symmbasis->kit();
|
---|
| 124 | bd3natom_ = symmbasis->coldim();
|
---|
| 125 | disym_ = symmbasis->rowdim();
|
---|
| 126 |
|
---|
| 127 | ExEnv::out0() << endl
|
---|
| 128 | << indent << "Frequencies (cm-1; negative is imaginary):"
|
---|
| 129 | << endl;
|
---|
| 130 |
|
---|
| 131 | // initialize the frequency tables
|
---|
| 132 | if (nfreq_) delete[] nfreq_;
|
---|
| 133 | nfreq_ = new int[nirrep_];
|
---|
| 134 | if (freq_) delete[] freq_;
|
---|
| 135 | freq_ = new double*[nirrep_];
|
---|
| 136 |
|
---|
| 137 | // initialize normal coordinate matrix
|
---|
| 138 | normco_ = symmatrixkit()->matrix(bd3natom_, disym_);
|
---|
| 139 |
|
---|
| 140 | // find the inverse sqrt mass matrix
|
---|
| 141 | RefDiagSCMatrix m(d3natom_, matrixkit());
|
---|
| 142 | for (i=0,coor=0; i<mol_->natom(); i++) {
|
---|
| 143 | for (int j=0; j<3; j++, coor++) {
|
---|
| 144 | m(coor) = 1.0/sqrt(mol_->mass(i)*(1.0/5.48579903e-4));
|
---|
| 145 | }
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 | RefSymmSCMatrix dhessian;
|
---|
| 149 |
|
---|
| 150 | for (int irrep=0; irrep<nirrep_; irrep++) {
|
---|
| 151 | RefSCMatrix dtranst = bsymmbasis->block(irrep);
|
---|
| 152 | RefSCDimension ddim = dtranst.rowdim();
|
---|
| 153 | nfreq_[irrep] = ddim.n();
|
---|
| 154 | freq_[irrep] = new double[nfreq_[irrep]];
|
---|
| 155 | if (ddim.n() == 0) continue;
|
---|
| 156 | dhessian = matrixkit()->symmmatrix(ddim);
|
---|
| 157 | dhessian.assign(0.0);
|
---|
| 158 | dhessian.accumulate_transform(dtranst,xhessian);
|
---|
| 159 | do_freq_for_irrep(irrep, m, dhessian, dtranst);
|
---|
| 160 | }
|
---|
| 161 | }
|
---|
| 162 |
|
---|
| 163 | void
|
---|
| 164 | MolecularFrequencies::do_freq_for_irrep(
|
---|
| 165 | int irrep,
|
---|
| 166 | const RefDiagSCMatrix &m,
|
---|
| 167 | const RefSymmSCMatrix &dhessian,
|
---|
| 168 | const RefSCMatrix &dtranst)
|
---|
| 169 | {
|
---|
| 170 | int i;
|
---|
| 171 | RefSCMatrix dtrans = dtranst.t();
|
---|
| 172 | RefSCDimension ddim = dtrans.coldim();
|
---|
| 173 | if (ddim.n() == 0) return;
|
---|
| 174 | if (debug_) {
|
---|
| 175 | dhessian.print("dhessian");
|
---|
| 176 | dtrans.print("dtrans");
|
---|
| 177 | }
|
---|
| 178 | // find the basis for the normal coordinates
|
---|
| 179 | RefSCMatrix ncbasis = m * dtrans;
|
---|
| 180 | // use the SVD to orthogonalize and check this basis
|
---|
| 181 | RefSCMatrix basU(d3natom_, d3natom_, matrixkit());
|
---|
| 182 | RefSCMatrix basV(ddim, ddim, matrixkit());
|
---|
| 183 | RefDiagSCMatrix bassigma(ddim, matrixkit());
|
---|
| 184 | ncbasis.svd(basU, bassigma, basV);
|
---|
| 185 | for (i=0; i<ddim.n(); i++) {
|
---|
| 186 | if (bassigma(i) < 1.e-3) {
|
---|
| 187 | throw ToleranceExceeded("singular value too small: "
|
---|
| 188 | "displacements don't span coordinates",
|
---|
| 189 | __FILE__, __LINE__, 1.e-3, bassigma(i),
|
---|
| 190 | class_desc());
|
---|
| 191 | }
|
---|
| 192 | }
|
---|
| 193 | ncbasis.assign_subblock(basU, 0, d3natom_.n()-1, 0, ddim.n()-1, 0, 0);
|
---|
| 194 | // a transform from disp to x to q (mass weighted x) to disp
|
---|
| 195 | RefSCMatrix dxqd = ncbasis.t() * m * dtrans;
|
---|
| 196 | // transform the dhessian to the mass weighted dhessian
|
---|
| 197 | RefSymmSCMatrix mdhessian = matrixkit()->symmmatrix(dxqd.rowdim());
|
---|
| 198 | mdhessian.assign(0.0);
|
---|
| 199 | mdhessian.accumulate_transform(dxqd, dhessian);
|
---|
| 200 | if (debug_) {
|
---|
| 201 | mdhessian.print("mass weighted dhessian");
|
---|
| 202 | }
|
---|
| 203 | // diagonalize the hessian
|
---|
| 204 | RefDiagSCMatrix freqs(ddim,matrixkit());
|
---|
| 205 | RefSCMatrix eigvecs(ddim,ddim,matrixkit());
|
---|
| 206 | mdhessian.diagonalize(freqs,eigvecs);
|
---|
| 207 | // convert the eigvals to frequencies in wavenumbers
|
---|
| 208 | for (i=0; i<freqs.n(); i++) {
|
---|
| 209 | if (freqs(i) >=0.0) freqs(i) = sqrt(freqs(i));
|
---|
| 210 | else freqs(i) = -sqrt(-freqs(i));
|
---|
| 211 | freq_[irrep][i] = freqs(i);
|
---|
| 212 | freqs(i) = freqs->get_element(i) * 219474.63;
|
---|
| 213 | }
|
---|
| 214 |
|
---|
| 215 | ExEnv::out0() << indent
|
---|
| 216 | << pg_->char_table().gamma(irrep).symbol() << endl;
|
---|
| 217 | int ifreqoff = 1;
|
---|
| 218 | for (i=0; i<irrep; i++) ifreqoff += nfreq_[i];
|
---|
| 219 | for (i=0; i<freqs.n(); i++) {
|
---|
| 220 | double freq = freqs(freqs.n()-i-1);
|
---|
| 221 | ExEnv::out0() << indent
|
---|
| 222 | << scprintf("%4d % 8.2f",i+ifreqoff,freq)
|
---|
| 223 | << endl;
|
---|
| 224 | }
|
---|
| 225 | ExEnv::out0() << endl;
|
---|
| 226 |
|
---|
| 227 | if (debug_) {
|
---|
| 228 | eigvecs.print("eigenvectors");
|
---|
| 229 | ncbasis.print("ncbasis");
|
---|
| 230 | (ncbasis*eigvecs).print("ncbasis*eigvecs");
|
---|
| 231 | }
|
---|
| 232 | dynamic_cast<BlockedSCMatrix*>(
|
---|
| 233 | normco_.pointer())->block(irrep).assign(ncbasis*eigvecs);
|
---|
| 234 | }
|
---|
| 235 |
|
---|
| 236 | void
|
---|
| 237 | MolecularFrequencies::thermochemistry(int degeneracy, double T, double P)
|
---|
| 238 | {
|
---|
| 239 | int i;
|
---|
| 240 | double tmpvar;
|
---|
| 241 |
|
---|
| 242 | if (!nfreq_) return;
|
---|
| 243 |
|
---|
| 244 | // default values for temperature T and pressure P are
|
---|
| 245 | // 298.15 K and 1 atm (=101325.0 Pa), respectively
|
---|
| 246 |
|
---|
| 247 | // 1986 CODATA
|
---|
| 248 | const double NA = 6.0221367e23; // Avogadro's number
|
---|
| 249 | const double k = 1.380658e-23; // Boltzmann's constant (J/K)
|
---|
| 250 | const double h = 6.6260755e-34; // Planck's constant (J*s)
|
---|
| 251 | const double R = 8.314510; // gas constant (J/(mol*K)) (R=k*NA)
|
---|
| 252 | const double pi = M_PI;
|
---|
| 253 | const double hartree_to_hertz = 6.5796838e15; // (hertz/hartree)
|
---|
| 254 |
|
---|
| 255 | const double hartree_to_joule = 4.3597482e-18; // (J/hartree)
|
---|
| 256 | const double hartree_to_joule_per_mol = hartree_to_joule*NA;
|
---|
| 257 | // (J/(mol*hartree))
|
---|
| 258 | const double amu_to_kg = 1.6605402e-27; // (kg/amu)
|
---|
| 259 | const double angstrom_to_meter = 1.0e-10;
|
---|
| 260 | const double atm_to_Pa = 101325.0; // (Pa/atm)
|
---|
| 261 |
|
---|
| 262 |
|
---|
| 263 | ////////////////////////////////////////////////////////////////////////
|
---|
| 264 | // compute the molar entropy using formulas for ideal polyatomic gasses
|
---|
| 265 | // from McQuarrie, Statistical Mechanics, 1976, Ch. 8; [use (8-27) for
|
---|
| 266 | // linear and (8-33) for non-linear molecules]
|
---|
| 267 | // S = S_trans + S_rot + S_vib + S_el
|
---|
| 268 | ////////////////////////////////////////////////////////////////////////
|
---|
| 269 |
|
---|
| 270 | // compute the mass of the molecule (in kg)
|
---|
| 271 | double mass = 0.0;
|
---|
| 272 | for (i=0; i<mol_->natom(); i++) {
|
---|
| 273 | mass += mol_->mass(i);
|
---|
| 274 | }
|
---|
| 275 | mass *= amu_to_kg;
|
---|
| 276 |
|
---|
| 277 | // compute principal moments of inertia (pmi) in amu*angstrom^2
|
---|
| 278 | double pmi[3];
|
---|
| 279 | mol_->principal_moments_of_inertia(pmi);
|
---|
| 280 |
|
---|
| 281 | // find out if molecule is linear (if smallest pmi < 1.0e-5 amu angstrom^2)
|
---|
| 282 | // (elements of pmi are sorted in order smallest to largest)
|
---|
| 283 | int linear = 0;
|
---|
| 284 | if (pmi[0] < 1.0e-5) linear = 1;
|
---|
| 285 |
|
---|
| 286 | // compute the symmetry number sigma;
|
---|
| 287 | // for linear molecules: sigma = 2 (D_inf_h), sigma = 1 (C_inf_v)
|
---|
| 288 | // for non-linear molecules: sigma = # of rot. in pt. grp, including E
|
---|
| 289 | int sigma;
|
---|
| 290 | CharacterTable ct = pg_->char_table();
|
---|
| 291 | if (linear) {
|
---|
| 292 | //if (D_inf_h) sigma = 2;
|
---|
| 293 | if (ct.symbol()[0] == 'D' ||
|
---|
| 294 | ct.symbol()[0] == 'd') sigma = 2;
|
---|
| 295 | else if (ct.symbol()[0] == 'C' ||
|
---|
| 296 | ct.symbol()[0] == 'c') sigma = 1;
|
---|
| 297 | else {
|
---|
| 298 | throw InputError("for linear molecules "
|
---|
| 299 | " the specified point group must be Cnv or Dnh",
|
---|
| 300 | __FILE__, __LINE__, 0, 0, class_desc());
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 | else if ((ct.symbol()[0] == 'C' ||
|
---|
| 304 | ct.symbol()[0] == 'c') &&
|
---|
| 305 | (ct.symbol()[1] >= '1' &&
|
---|
| 306 | ct.symbol()[1] <= '8') &&
|
---|
| 307 | ct.symbol()[2] == '\0') {
|
---|
| 308 | sigma = ct.order(); // group is a valid CN
|
---|
| 309 | }
|
---|
| 310 | else if ((ct.symbol()[0] == 'D' ||
|
---|
| 311 | ct.symbol()[0] == 'd') &&
|
---|
| 312 | (ct.symbol()[1] >= '2' &&
|
---|
| 313 | ct.symbol()[1] <= '6') &&
|
---|
| 314 | ct.symbol()[2] == '\0') {
|
---|
| 315 | sigma = ct.order(); // group is a valid DN
|
---|
| 316 | }
|
---|
| 317 | else if ((ct.symbol()[0] == 'T' ||
|
---|
| 318 | ct.symbol()[0] == 't') &&
|
---|
| 319 | ct.symbol()[1] == '\0') {
|
---|
| 320 | sigma = ct.order(); // group is T
|
---|
| 321 | }
|
---|
| 322 | else sigma = (int)(0.5*ct.order()); // group is not pure rot. group (CN, DN, or T)
|
---|
| 323 |
|
---|
| 324 | // compute S_trans
|
---|
| 325 | double S_trans;
|
---|
| 326 | tmpvar = pow(2*pi*mass*k*T/(h*h),1.5);
|
---|
| 327 | S_trans = R*(log(tmpvar*R*T/(P*atm_to_Pa)) + 2.5 - log(NA));
|
---|
| 328 |
|
---|
| 329 | // compute S_rot
|
---|
| 330 | double S_rot;
|
---|
| 331 | double theta[3]; // rotational temperatures (K)
|
---|
| 332 | if (linear) {
|
---|
| 333 | theta[1] = h*h/(8*pi*pi*pmi[1]*amu_to_kg*pow(angstrom_to_meter,2.0)*k);
|
---|
| 334 | S_rot = log(T/(sigma*theta[1])) + 1.0;
|
---|
| 335 | }
|
---|
| 336 | else {
|
---|
| 337 | theta[0] = h*h/(8*pi*pi*pmi[0]*amu_to_kg*pow(angstrom_to_meter,2.0)*k);
|
---|
| 338 | theta[1] = h*h/(8*pi*pi*pmi[1]*amu_to_kg*pow(angstrom_to_meter,2.0)*k);
|
---|
| 339 | theta[2] = h*h/(8*pi*pi*pmi[2]*amu_to_kg*pow(angstrom_to_meter,2.0)*k);
|
---|
| 340 | tmpvar = theta[0]*theta[1]*theta[2];
|
---|
| 341 | S_rot = log(pow(pi*T*T*T/tmpvar,0.5)/sigma) + 1.5;
|
---|
| 342 | }
|
---|
| 343 | S_rot *= R;
|
---|
| 344 |
|
---|
| 345 | // compute S_vib
|
---|
| 346 | double S_vib = 0.0;
|
---|
| 347 | for (i=0; i<nirrep_; i++) {
|
---|
| 348 | for (int j=0; j<nfreq_[i]; j++) {
|
---|
| 349 | if (freq_[i][j] > 0.0) {
|
---|
| 350 | tmpvar = hartree_to_hertz*h*freq_[i][j]/(k*T);
|
---|
| 351 | double expval = exp(-tmpvar);
|
---|
| 352 | S_vib += tmpvar*expval/(1.0-expval) - log(1.0-expval);
|
---|
| 353 | }
|
---|
| 354 | }
|
---|
| 355 | }
|
---|
| 356 | S_vib *= R;
|
---|
| 357 |
|
---|
| 358 | // compute S_el
|
---|
| 359 | double S_el;
|
---|
| 360 | S_el = R*log(double(degeneracy));
|
---|
| 361 |
|
---|
| 362 | // compute total molar entropy S (in J/(mol*K))
|
---|
| 363 | double S;
|
---|
| 364 | S = S_trans + S_rot + S_vib + S_el;
|
---|
| 365 |
|
---|
| 366 |
|
---|
| 367 | //////////////////////////////////////////////
|
---|
| 368 | // compute the molar enthalpy (nonelectronic)
|
---|
| 369 | //////////////////////////////////////////////
|
---|
| 370 |
|
---|
| 371 | int n_zero_or_imaginary = 0;
|
---|
| 372 | double E0vib = 0.0;
|
---|
| 373 | for (i=0; i<nirrep_; i++) {
|
---|
| 374 | for (int j=0; j<nfreq_[i]; j++) {
|
---|
| 375 | if (freq_[i][j] > 0.0) E0vib += freq_[i][j] * hartree_to_joule_per_mol;
|
---|
| 376 | else n_zero_or_imaginary++;
|
---|
| 377 | }
|
---|
| 378 | }
|
---|
| 379 | E0vib *= 0.5;
|
---|
| 380 |
|
---|
| 381 | double EvibT = 0.0;
|
---|
| 382 | for (i=0; i<nirrep_; i++) {
|
---|
| 383 | for (int j=0; j<nfreq_[i]; j++) {
|
---|
| 384 | if (freq_[i][j] > 0.0) {
|
---|
| 385 | double expval = exp(-freq_[i][j]*hartree_to_joule/(k*T));
|
---|
| 386 | EvibT += freq_[i][j] * hartree_to_joule_per_mol
|
---|
| 387 | * expval/(1.0-expval);
|
---|
| 388 | }
|
---|
| 389 | }
|
---|
| 390 | }
|
---|
| 391 |
|
---|
| 392 | double EPV = NA*k*T;
|
---|
| 393 |
|
---|
| 394 | int nexternal = 6;
|
---|
| 395 | if (mol_->natom() == 1) nexternal = 3;
|
---|
| 396 | else if (mol_->is_linear()) nexternal = 5;
|
---|
| 397 |
|
---|
| 398 | double Erot;
|
---|
| 399 | if (nexternal == 3) {
|
---|
| 400 | // atom
|
---|
| 401 | Erot = 0.0;
|
---|
| 402 | }
|
---|
| 403 | else if (nexternal == 5) {
|
---|
| 404 | // linear
|
---|
| 405 | Erot = EPV;
|
---|
| 406 | }
|
---|
| 407 | else if (nexternal == 6) {
|
---|
| 408 | // nonlinear
|
---|
| 409 | Erot = 1.5 * EPV;
|
---|
| 410 | }
|
---|
| 411 | else {
|
---|
| 412 | ExEnv::errn() << "Strange number of external coordinates: " << nexternal
|
---|
| 413 | << ". Setting Erot to 0.0" << endl;
|
---|
| 414 | Erot = 0.0;
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | double Etrans = 1.5 * EPV;
|
---|
| 418 |
|
---|
| 419 | ////////////////////////////////////////////////
|
---|
| 420 | // Print out results of thermodynamic analysis
|
---|
| 421 | ////////////////////////////////////////////////
|
---|
| 422 |
|
---|
| 423 | ExEnv::out0() << indent << "THERMODYNAMIC ANALYSIS:" << endl << endl
|
---|
| 424 | << indent << scprintf("Contributions to the nonelectronic enthalpy at %.2lf K:\n",T)
|
---|
| 425 | << indent << " kJ/mol kcal/mol"<< endl
|
---|
| 426 | << indent << scprintf(" E0vib = %9.4lf %9.4lf\n",
|
---|
| 427 | E0vib/1000, E0vib/(4.184*1000))
|
---|
| 428 | << indent << scprintf(" Evib(T) = %9.4lf %9.4lf\n",
|
---|
| 429 | EvibT/1000, EvibT/(4.184*1000))
|
---|
| 430 | << indent << scprintf(" Erot(T) = %9.4lf %9.4lf\n",
|
---|
| 431 | Erot/1000, Erot/(4.184*1000))
|
---|
| 432 | << indent << scprintf(" Etrans(T) = %9.4lf %9.4lf\n",
|
---|
| 433 | Etrans/1000, Etrans/(4.184*1000))
|
---|
| 434 | << indent << scprintf(" PV(T) = %9.4lf %9.4lf\n",
|
---|
| 435 | EPV/1000, EPV/(4.184*1000))
|
---|
| 436 | << indent << scprintf(" Total nonelectronic enthalpy:\n")
|
---|
| 437 | << indent << scprintf(" H_nonel(T) = %9.4lf %9.4lf\n",
|
---|
| 438 | (E0vib+EvibT+Erot+Etrans+EPV)/1000,
|
---|
| 439 | (E0vib+EvibT+Erot+Etrans+EPV)/(4.184*1000))
|
---|
| 440 | << endl
|
---|
| 441 | << indent
|
---|
| 442 | << scprintf("Contributions to the entropy at %.2lf K and %.1lf atm:\n",
|
---|
| 443 | T, P)
|
---|
| 444 | << indent << " J/(mol*K) cal/(mol*K)"<< endl
|
---|
| 445 | << indent
|
---|
| 446 | << scprintf(" S_trans(T,P) = %9.4lf %9.4lf\n",
|
---|
| 447 | S_trans, S_trans/4.184)
|
---|
| 448 | << indent
|
---|
| 449 | << scprintf(" S_rot(T) = %9.4lf %9.4lf\n", S_rot,S_rot/4.184)
|
---|
| 450 | << indent
|
---|
| 451 | << scprintf(" S_vib(T) = %9.4lf %9.4lf\n", S_vib,S_vib/4.184)
|
---|
| 452 | << indent
|
---|
| 453 | << scprintf(" S_el = %9.4lf %9.4lf\n", S_el,S_el/4.184)
|
---|
| 454 | << indent << scprintf(" Total entropy:\n")
|
---|
| 455 | << indent << scprintf(" S_total(T,P) = %9.4lf %9.4lf\n", S, S/4.184)
|
---|
| 456 | << indent << endl
|
---|
| 457 |
|
---|
| 458 | << indent << "Various data used for thermodynamic analysis:" << endl
|
---|
| 459 | << indent << endl;
|
---|
| 460 |
|
---|
| 461 | if (linear) ExEnv::out0() << indent << "Linear molecule" << endl;
|
---|
| 462 | else ExEnv::out0() << indent << "Nonlinear molecule" << endl;
|
---|
| 463 |
|
---|
| 464 | ExEnv::out0() << indent
|
---|
| 465 | << scprintf("Principal moments of inertia (amu*angstrom^2):"
|
---|
| 466 | " %.5lf, %.5lf, %.5lf\n", pmi[0], pmi[1], pmi[2])
|
---|
| 467 | << indent << "Point group: " << ct.symbol()
|
---|
| 468 | << endl
|
---|
| 469 | << indent << "Order of point group: " << ct.order() << endl
|
---|
| 470 | << indent << "Rotational symmetry number: " << sigma << endl;
|
---|
| 471 |
|
---|
| 472 | if (linear) {
|
---|
| 473 | ExEnv::out0() << indent
|
---|
| 474 | << scprintf("Rotational temperature (K): %.4lf\n", theta[1]);
|
---|
| 475 | }
|
---|
| 476 | else {
|
---|
| 477 | ExEnv::out0() << indent
|
---|
| 478 | << scprintf("Rotational temperatures (K): %.4lf, %.4lf, %.4lf\n",
|
---|
| 479 | theta[0], theta[1], theta[2]);
|
---|
| 480 | }
|
---|
| 481 |
|
---|
| 482 | ExEnv::out0() << indent << "Electronic degeneracy: " << degeneracy
|
---|
| 483 | << endl << endl;
|
---|
| 484 | }
|
---|
| 485 |
|
---|
| 486 | void
|
---|
| 487 | MolecularFrequencies::animate(const Ref<Render>& render,
|
---|
| 488 | const Ref<MolFreqAnimate>& anim)
|
---|
| 489 | {
|
---|
| 490 | int i,j, symoff = 0;
|
---|
| 491 | for (i=0; i<nirrep_; i++) {
|
---|
| 492 | int nfreq = disym_->blocks()->size(i);
|
---|
| 493 | for (j=0; j<nfreq; j++) {
|
---|
| 494 | char name[128];
|
---|
| 495 | sprintf(name,"%02d.%s",
|
---|
| 496 | nfreq-j+symoff, pg_->char_table().gamma(i).symbol_ns());
|
---|
| 497 | anim->set_name(name);
|
---|
| 498 | anim->set_mode(i,j);
|
---|
| 499 | render->animate(anim.pointer());
|
---|
| 500 | }
|
---|
| 501 | symoff += nfreq;
|
---|
| 502 | }
|
---|
| 503 | }
|
---|
| 504 |
|
---|
| 505 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 506 | // MolFreqAnimate
|
---|
| 507 |
|
---|
| 508 | static ClassDesc MolFreqAnimate_cd(
|
---|
| 509 | typeid(MolFreqAnimate),"MolFreqAnimate",1,"public AnimatedObject",
|
---|
| 510 | 0, create<MolFreqAnimate>, 0);
|
---|
| 511 |
|
---|
| 512 | MolFreqAnimate::MolFreqAnimate(const Ref<KeyVal> &keyval):
|
---|
| 513 | AnimatedObject(keyval)
|
---|
| 514 | {
|
---|
| 515 | renmol_ << keyval->describedclassvalue("rendered");
|
---|
| 516 | molfreq_ << keyval->describedclassvalue("freq");
|
---|
| 517 | dependent_mole_ << keyval->describedclassvalue("dependent_mole");
|
---|
| 518 | irrep_ = keyval->intvalue("irrep");
|
---|
| 519 | mode_ = keyval->intvalue("mode");
|
---|
| 520 | KeyValValueint default_nframe(10);
|
---|
| 521 | nframe_ = keyval->intvalue("nframe",default_nframe);
|
---|
| 522 | KeyValValuedouble default_disp(0.2);
|
---|
| 523 | disp_ = keyval->doublevalue("displacement", default_disp);
|
---|
| 524 | }
|
---|
| 525 |
|
---|
| 526 | MolFreqAnimate::~MolFreqAnimate()
|
---|
| 527 | {
|
---|
| 528 | }
|
---|
| 529 |
|
---|
| 530 | int
|
---|
| 531 | MolFreqAnimate::nobject()
|
---|
| 532 | {
|
---|
| 533 | return nframe_;
|
---|
| 534 | }
|
---|
| 535 |
|
---|
| 536 | Ref<RenderedObject>
|
---|
| 537 | MolFreqAnimate::object(int iobject)
|
---|
| 538 | {
|
---|
| 539 | BlockedSCMatrix *normco
|
---|
| 540 | = dynamic_cast<BlockedSCMatrix*>(molfreq_->normal_coordinates().pointer());
|
---|
| 541 | Ref<Molecule> mol = renmol_->molecule();
|
---|
| 542 | Ref<Molecule> molcopy = new Molecule(*mol.pointer());
|
---|
| 543 |
|
---|
| 544 | double scale = disp_ * cos(M_PI*(iobject+0.5)/(double)nframe_);
|
---|
| 545 |
|
---|
| 546 | RefSCMatrix irrepblock = normco->block(irrep_);
|
---|
| 547 | int ixyz, iatom, icoor=0;
|
---|
| 548 | for (iatom=0; iatom<mol->natom(); iatom++) {
|
---|
| 549 | for (ixyz=0; ixyz<3; ixyz++, icoor++) {
|
---|
| 550 | mol->r(iatom,ixyz) += scale
|
---|
| 551 | * irrepblock->get_element(icoor,mode_);
|
---|
| 552 | }
|
---|
| 553 | }
|
---|
| 554 |
|
---|
| 555 | if (dependent_mole_.nonnull()) dependent_mole_->obsolete();
|
---|
| 556 | renmol_->init();
|
---|
| 557 |
|
---|
| 558 | char name[64];
|
---|
| 559 | sprintf(name,"%02d",iobject);
|
---|
| 560 | renmol_->set_name(name);
|
---|
| 561 |
|
---|
| 562 | // restore the original molecule
|
---|
| 563 | mol->operator = (*molcopy.pointer());
|
---|
| 564 | if (dependent_mole_.nonnull()) dependent_mole_->obsolete();
|
---|
| 565 |
|
---|
| 566 | return renmol_.pointer();
|
---|
| 567 | }
|
---|
| 568 |
|
---|
| 569 |
|
---|
| 570 | /////////////////////////////////////////////////////////////////////////////
|
---|
| 571 |
|
---|
| 572 | // Local Variables:
|
---|
| 573 | // mode: c++
|
---|
| 574 | // c-file-style: "CLJ"
|
---|
| 575 | // End:
|
---|