source: ThirdParty/mpqc_open/src/lib/chemistry/molecule/molecule.cc@ 7516f6

Action_Thermostats Adding_MD_integration_tests Adding_StructOpt_integration_tests AutomationFragmentation_failures Candidate_v1.6.1 ChemicalSpaceEvaluator Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Exclude_Hydrogens_annealWithBondGraph Fix_Verbose_Codepatterns ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion Gui_displays_atomic_force_velocity JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool PythonUI_with_named_parameters Recreated_GuiChecks StoppableMakroAction TremoloParser_IncreasedPrecision
Last change on this file since 7516f6 was 860145, checked in by Frederik Heber <heber@…>, 8 years ago

Merge commit '0b990dfaa8c6007a996d030163a25f7f5fc8a7e7' as 'ThirdParty/mpqc_open'

  • Property mode set to 100644
File size: 36.4 KB
Line 
1//
2// molecule.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 <math.h>
33#include <string.h>
34#include <stdio.h>
35
36#include <util/class/scexception.h>
37#include <util/misc/formio.h>
38#include <util/state/stateio.h>
39#include <chemistry/molecule/molecule.h>
40#include <chemistry/molecule/formula.h>
41#include <chemistry/molecule/localdef.h>
42#include <math/scmat/cmatrix.h>
43
44using namespace std;
45using namespace sc;
46
47//////////////////////////////////////////////////////////////////////////
48// Molecule
49
50static ClassDesc Molecule_cd(
51 typeid(Molecule),"Molecule",6,"public SavableState",
52 create<Molecule>, create<Molecule>, create<Molecule>);
53
54Molecule::Molecule():
55 natoms_(0), r_(0), Z_(0), charges_(0), mass_(0), labels_(0)
56{
57 pg_ = new PointGroup;
58 atominfo_ = new AtomInfo();
59 geometry_units_ = new Units("bohr");
60 nuniq_ = 0;
61 equiv_ = 0;
62 nequiv_ = 0;
63 atom_to_uniq_ = 0;
64 q_Z_ = atominfo_->string_to_Z("Q");
65 include_q_ = false;
66 include_qq_ = false;
67 init_symmetry_info();
68}
69
70Molecule::Molecule(const Molecule& mol):
71 natoms_(0), r_(0), Z_(0), charges_(0), mass_(0), labels_(0)
72{
73 nuniq_ = 0;
74 equiv_ = 0;
75 nequiv_ = 0;
76 atom_to_uniq_ = 0;
77 *this=mol;
78}
79
80Molecule::~Molecule()
81{
82 clear();
83}
84
85void
86Molecule::clear()
87{
88 if (r_) {
89 delete[] r_[0];
90 delete[] r_;
91 r_ = 0;
92 }
93 if (labels_) {
94 for (int i=0; i<natoms_; i++) {
95 delete[] labels_[i];
96 }
97 delete[] labels_;
98 labels_ = 0;
99 }
100 delete[] charges_;
101 charges_ = 0;
102 delete[] mass_;
103 mass_ = 0;
104 delete[] Z_;
105 Z_ = 0;
106
107 clear_symmetry_info();
108}
109
110void
111Molecule::throw_if_atom_duplicated(int begin, double tol)
112{
113 for (int i=begin; i<natoms_; i++) {
114 SCVector3 ri(r_[i]);
115 for (int j=0; j<i; j++) {
116 SCVector3 rj(r_[j]);
117 if (ri.dist(rj) < tol) {
118 throw InputError("duplicated atom coordinate",
119 __FILE__, __LINE__, 0, 0, class_desc());
120 }
121 }
122 }
123}
124
125Molecule::Molecule(const Ref<KeyVal>&input):
126 natoms_(0), r_(0), Z_(0), charges_(0), mass_(0), labels_(0)
127{
128 nuniq_ = 0;
129 equiv_ = 0;
130 nequiv_ = 0;
131 atom_to_uniq_ = 0;
132
133 KeyValValueboolean kvfalse(0);
134 include_q_ = input->booleanvalue("include_q",kvfalse);
135 include_qq_ = input->booleanvalue("include_qq",kvfalse);
136
137 atominfo_ << input->describedclassvalue("atominfo");
138 if (atominfo_.null()) atominfo_ = new AtomInfo;
139 q_Z_ = atominfo_->string_to_Z("Q");
140 if (input->exists("pdb_file")) {
141 geometry_units_ = new Units("angstrom");
142 char* filename = input->pcharvalue("pdb_file");
143 read_pdb(filename);
144 delete[] filename;
145 }
146 else {
147 // check for old style units input first
148 if (input->booleanvalue("angstrom")
149 ||input->booleanvalue("angstroms")
150 ||input->booleanvalue("aangstrom")
151 ||input->booleanvalue("aangstroms")) {
152 geometry_units_ = new Units("angstrom");
153 }
154 // check for new style units input
155 else {
156 geometry_units_ = new Units(input->pcharvalue("unit"),
157 Units::Steal);
158 }
159
160 double conv = geometry_units_->to_atomic_units();
161
162 // get the number of atoms and make sure that the geometry and the
163 // atoms array have the same number of atoms.
164 // right now we read in the unique atoms...then we will symmetrize.
165 // the length of atoms must still equal the length of geometry, but
166 // we'll try to set up atom_labels such that different lengths are
167 // possible
168 int natom = input->count("geometry");
169 if (natom != input->count("atoms")) {
170 throw InputError("size of \"geometry\" != size of \"atoms\"",
171 __FILE__, __LINE__, 0, 0, class_desc());
172 }
173
174 int i;
175 for (i=0; i<natom; i++) {
176 char *name, *label;
177 int ghost = input->booleanvalue("ghost",i);
178 double charge = input->doublevalue("charge",i);
179 int have_charge = input->error() == KeyVal::OK;
180 if (ghost) {
181 have_charge = 1;
182 charge = 0.0;
183 }
184 add_atom(atominfo_->string_to_Z(name = input->pcharvalue("atoms",i)),
185 input->doublevalue("geometry",i,0)*conv,
186 input->doublevalue("geometry",i,1)*conv,
187 input->doublevalue("geometry",i,2)*conv,
188 label = input->pcharvalue("atom_labels",i),
189 input->doublevalue("mass",i),
190 have_charge, charge
191 );
192 delete[] name;
193 delete[] label;
194 }
195 }
196
197 char *symmetry = input->pcharvalue("symmetry");
198 double symtol = input->doublevalue("symmetry_tolerance",
199 KeyValValuedouble(1.0e-4));
200 nuniq_ = 0;
201 equiv_ = 0;
202 nequiv_ = 0;
203 atom_to_uniq_ = 0;
204 if (symmetry && !strcmp(symmetry,"auto")) {
205 set_point_group(highest_point_group(symtol), symtol*10.0);
206 }
207 else {
208 pg_ = new PointGroup(input);
209
210 // translate to the origin of the symmetry frame
211 double r[3];
212 for (int i=0; i<3; i++) {
213 r[i] = -pg_->origin()[i];
214 pg_->origin()[i] = 0;
215 }
216 translate(r);
217
218 if (input->booleanvalue("redundant_atoms")) {
219 init_symmetry_info();
220 cleanup_molecule(symtol);
221 }
222 else {
223 symmetrize();
224 // In case we were given redundant atoms, clean up
225 // the geometry so the symmetry is exact.
226 cleanup_molecule(symtol);
227 }
228 }
229 delete[] symmetry;
230}
231
232Molecule&
233Molecule::operator=(const Molecule& mol)
234{
235 clear();
236
237 pg_ = new PointGroup(*(mol.pg_.pointer()));
238 atominfo_ = mol.atominfo_;
239 geometry_units_ = new Units(mol.geometry_units_->string_rep());
240
241 q_Z_ = mol.q_Z_;
242 include_q_ = mol.include_q_;
243 include_qq_ = mol.include_qq_;
244 q_atoms_ = mol.q_atoms_;
245 non_q_atoms_ = mol.non_q_atoms_;
246
247 natoms_ = mol.natoms_;
248
249 if (natoms_) {
250 if (mol.mass_) {
251 mass_ = new double[natoms_];
252 memcpy(mass_,mol.mass_,natoms_*sizeof(double));
253 }
254 if (mol.charges_) {
255 charges_ = new double[natoms_];
256 memcpy(charges_,mol.charges_,natoms_*sizeof(double));
257 }
258 if (mol.labels_) {
259 labels_ = new char *[natoms_];
260 for (int i=0; i<natoms_; i++) {
261 if (mol.labels_[i]) {
262 labels_[i] = strcpy(new char[strlen(mol.labels_[i])+1],
263 mol.labels_[i]);
264 }
265 else labels_[i] = 0;
266 }
267 }
268 r_ = new double*[natoms_];
269 r_[0] = new double[natoms_*3];
270 for (int i=0; i<natoms_; i++) {
271 r_[i] = &(r_[0][i*3]);
272 }
273 memcpy(r_[0], mol.r_[0], natoms_*3*sizeof(double));
274 Z_ = new int[natoms_];
275 memcpy(Z_, mol.Z_, natoms_*sizeof(int));
276 }
277
278 init_symmetry_info();
279
280 return *this;
281}
282
283void
284Molecule::add_atom(int Z,double x,double y,double z,
285 const char *label,double mass,
286 int have_charge, double charge)
287{
288 int i;
289
290 // allocate new arrays
291 int *newZ = new int[natoms_+1];
292 double **newr = new double*[natoms_+1];
293 double *newr0 = new double[(natoms_+1)*3];
294 char **newlabels = 0;
295 if (label || labels_) {
296 newlabels = new char*[natoms_+1];
297 }
298 double *newcharges = 0;
299 if (have_charge || charges_) {
300 newcharges = new double[natoms_+1];
301 }
302 double *newmass = 0;
303 if (mass_ || mass != 0.0) {
304 newmass = new double[natoms_+1];
305 }
306
307 // setup the r_ pointers
308 for (i=0; i<=natoms_; i++) {
309 newr[i] = &(newr0[i*3]);
310 }
311
312 // copy old data to new arrays
313 if (natoms_) {
314 memcpy(newZ,Z_,sizeof(int)*natoms_);
315 memcpy(newr0,r_[0],sizeof(double)*natoms_*3);
316 if (labels_) {
317 memcpy(newlabels,labels_,sizeof(char*)*natoms_);
318 }
319 else if (newlabels) {
320 memset(newlabels,0,sizeof(char*)*natoms_);
321 }
322 if (charges_) {
323 memcpy(newcharges,charges_,sizeof(double)*natoms_);
324 }
325 else if (newcharges) {
326 for (i=0; i<natoms_; i++) newcharges[i] = Z_[i];
327 }
328 if (mass_) {
329 memcpy(newmass,mass_,sizeof(double)*natoms_);
330 }
331 else if (newmass) {
332 memset(newmass,0,sizeof(double)*natoms_);
333 }
334 }
335
336 // delete old data
337 delete[] Z_;
338 if (r_) {
339 delete[] r_[0];
340 delete[] r_;
341 }
342 delete[] labels_;
343 delete[] charges_;
344 delete[] mass_;
345
346 // setup new pointers
347 Z_ = newZ;
348 r_ = newr;
349 labels_ = newlabels;
350 charges_ = newcharges;
351 mass_ = newmass;
352
353 // copy info for this atom into arrays
354 Z_[natoms_] = Z;
355 r_[natoms_][0] = x;
356 r_[natoms_][1] = y;
357 r_[natoms_][2] = z;
358 if (mass_) mass_[natoms_] = mass;
359 if (label) {
360 labels_[natoms_] = strcpy(new char[strlen(label)+1],label);
361 }
362 else if (labels_) {
363 labels_[natoms_] = 0;
364 }
365 if (have_charge) {
366 charges_[natoms_] = charge;
367 }
368 else if (charges_) {
369 charges_[natoms_] = Z;
370 }
371
372 if (Z == q_Z_) {
373 q_atoms_.push_back(natoms_);
374 }
375 else {
376 non_q_atoms_.push_back(natoms_);
377 }
378
379 natoms_++;
380
381 throw_if_atom_duplicated(natoms_-1);
382}
383
384void
385Molecule::print_parsedkeyval(ostream& os,
386 int print_pg,
387 int print_unit,
388 int number_atoms) const
389{
390 int i;
391
392 double conv = geometry_units_->from_atomic_units();
393
394 if (print_pg) pg_->print(os);
395 if (print_unit && geometry_units_->string_rep()) {
396 os << indent
397 << "unit = \"" << geometry_units_->string_rep() << "\""
398 << endl;
399 }
400 os << indent << "{";
401 if (number_atoms) os << scprintf("%3s", "n");
402 os << scprintf(" %5s", "atoms");
403 if (labels_) os << scprintf(" %11s", "atom_labels");
404 int int_charges = 1;
405 if (charges_) {
406 for (i=0;i<natom();i++) if (charges_[i]!=(int)charges_[i]) int_charges=0;
407 if (int_charges) {
408 os << scprintf(" %7s", "charge");
409 }
410 else {
411 os << scprintf(" %17s", "charge");
412 }
413 }
414 os << scprintf(" %16s", "")
415 << scprintf(" %16s", "geometry ")
416 << scprintf(" %16s ", "");
417 os << "}={" << endl;
418 for (i=0; i<natom(); i++) {
419 os << indent;
420 if (number_atoms) os << scprintf(" %3d", i+1);
421 std::string symbol(atom_symbol(i));
422 os << scprintf(" %5s", symbol.c_str());
423 if (labels_) {
424 const char *lab = labels_[i];
425 if (lab == 0) lab = "";
426 char *qlab = new char[strlen(lab)+3];
427 strcpy(qlab,"\"");
428 strcat(qlab,lab);
429 strcat(qlab,"\"");
430 os << scprintf(" %11s",qlab);
431 delete[] qlab;
432 }
433 if (charges_) {
434 if (int_charges) os << scprintf(" %7.4f", charges_[i]);
435 else os << scprintf(" %17.15f", charges_[i]);
436 }
437 os << scprintf(" [% 16.10f", conv * r(i,0))
438 << scprintf(" % 16.10f", conv * r(i,1))
439 << scprintf(" % 16.10f]", conv * r(i,2))
440 << endl;
441 }
442 os << indent << "}" << endl;
443}
444
445void
446Molecule::print(ostream& os) const
447{
448 int i;
449
450 MolecularFormula *mf = new MolecularFormula(this);
451 os << indent
452 << "Molecular formula: " << mf->formula() << endl;
453 delete mf;
454
455 os << indent << "molecule<Molecule>: (" << endl;
456 os << incindent;
457 print_parsedkeyval(os);
458 os << decindent;
459 os << indent << ")" << endl;
460
461 os << indent << "Atomic Masses:" << endl;
462 for (i=0; i<natom(); i+=5) {
463 os << indent;
464 for (int j=i; j<i+5 && j<natom(); j++) {
465 os << scprintf(" %10.5f", mass(j));
466 }
467 os << endl;
468 }
469}
470
471int
472Molecule::atom_label_to_index(const char *l) const
473{
474 int i;
475 for (i=0; i<natom(); i++) {
476 if (label(i) && !strcmp(l,label(i))) return i;
477 }
478 return -1;
479}
480
481double*
482Molecule::charges() const
483{
484 double*result = new double[natoms_];
485 if (charges_) {
486 memcpy(result, charges_, sizeof(double)*natom());
487 }
488 else {
489 for (int i=0; i<natom(); i++) result[i] = Z_[i];
490 }
491 return result;
492}
493
494double
495Molecule::charge(int iatom) const
496{
497 if (charges_) return charges_[iatom];
498 return Z_[iatom];
499}
500
501double
502Molecule::nuclear_charge() const
503{
504 double c = 0.0;
505 if (include_q_) {
506 for (int i=0; i<natom(); i++) {
507 c += charge(i);
508 }
509 }
510 else {
511 for (int ii=0; ii<non_q_atoms_.size(); ii++) {
512 c += charge(non_q_atoms_[ii]);
513 }
514 }
515 return c;
516}
517
518void Molecule::save_data_state(StateOut& so)
519{
520 so.put(include_q_);
521 so.put(include_qq_);
522 so.put(natoms_);
523 SavableState::save_state(pg_.pointer(),so);
524 SavableState::save_state(geometry_units_.pointer(),so);
525 SavableState::save_state(atominfo_.pointer(),so);
526 if (natoms_) {
527 so.put(Z_, natoms_);
528 so.put_array_double(r_[0], natoms_*3);
529 so.put(charges_,natoms_);
530 }
531 if (mass_) {
532 so.put(1);
533 so.put_array_double(mass_, natoms_);
534 }
535 else {
536 so.put(0);
537 }
538 if (labels_){
539 so.put(1);
540 for (int i=0; i<natoms_; i++) {
541 so.putstring(labels_[i]);
542 }
543 }
544 else {
545 so.put(0);
546 }
547}
548
549Molecule::Molecule(StateIn& si):
550 SavableState(si),
551 natoms_(0), r_(0), Z_(0), mass_(0), labels_(0)
552{
553 if (si.version(::class_desc<Molecule>()) < 4) {
554 throw FileOperationFailed("cannot restore from old molecules",
555 __FILE__, __LINE__, 0,
556 FileOperationFailed::Corrupt,
557 class_desc());
558 }
559 if (si.version(::class_desc<Molecule>()) < 6) {
560 include_q_ = false;
561 include_qq_ = false;
562 }
563 else {
564 si.get(include_q_);
565 si.get(include_qq_);
566 }
567 si.get(natoms_);
568 pg_ << SavableState::restore_state(si);
569 geometry_units_ << SavableState::restore_state(si);
570 atominfo_ << SavableState::restore_state(si);
571 if (natoms_) {
572 si.get(Z_);
573 r_ = new double*[natoms_];
574 r_[0] = new double[natoms_*3];
575 si.get_array_double(r_[0],natoms_*3);
576 for (int i=1; i<natoms_; i++) {
577 r_[i] = &(r_[0][i*3]);
578 }
579 if (si.version(::class_desc<Molecule>()) > 4) {
580 si.get(charges_);
581 }
582 else {
583 charges_ = 0;
584 }
585 }
586 int test;
587 si.get(test);
588 if (test) {
589 mass_ = new double[natoms_];
590 si.get_array_double(mass_, natoms_);
591 }
592 si.get(test);
593 if (test){
594 labels_ = new char*[natoms_];
595 for (int i=0; i<natoms_; i++) {
596 si.getstring(labels_[i]);
597 }
598 }
599
600 for (int i=0; i<natoms_; i++) {
601 if (Z_[i] == q_Z_) {
602 q_atoms_.push_back(i);
603 }
604 else {
605 non_q_atoms_.push_back(i);
606 }
607 }
608
609 nuniq_ = 0;
610 equiv_ = 0;
611 nequiv_ = 0;
612 atom_to_uniq_ = 0;
613 init_symmetry_info();
614}
615
616int
617Molecule::atom_to_unique_offset(int iatom) const
618{
619 int iuniq = atom_to_uniq_[iatom];
620 int nequiv = nequiv_[iuniq];
621 for (int i=0; i<nequiv; i++) {
622 if (equiv_[iuniq][i] == iatom) return i;
623 }
624 ExEnv::errn() << "Molecule::atom_to_unique_offset: internal error"
625 << endl;
626 return -1;
627}
628
629void
630Molecule::set_point_group(const Ref<PointGroup>&ppg, double tol)
631{
632 ExEnv::out0() << indent
633 << "Molecule: setting point group to " << ppg->symbol()
634 << endl;
635 pg_ = new PointGroup(*ppg.pointer());
636
637 double r[3];
638 for (int i=0; i<3; i++) {
639 r[i] = -pg_->origin()[i];
640 pg_->origin()[i] = 0;
641 }
642 translate(r);
643
644 clear_symmetry_info();
645 init_symmetry_info();
646
647 cleanup_molecule(tol);
648}
649
650Ref<PointGroup>
651Molecule::point_group() const
652{
653 return pg_;
654}
655
656SCVector3
657Molecule::center_of_mass() const
658{
659 SCVector3 ret;
660 double M;
661
662 ret = 0.0;
663 M = 0.0;
664
665 for (int i=0; i < natom(); i++) {
666 double m = mass(i);
667 ret += m * SCVector3(r(i));
668 M += m;
669 }
670
671 ret *= 1.0/M;
672
673 return ret;
674}
675
676double
677Molecule::nuclear_repulsion_energy()
678{
679 double e=0.0;
680
681 // non_q non_q terms
682 for (int ii=1; ii < non_q_atoms_.size(); ii++) {
683 int i = non_q_atoms_[ii];
684 SCVector3 ai(r(i));
685 double Zi = charge(i);
686
687 for (int jj=0; jj < ii; jj++) {
688 int j = non_q_atoms_[jj];
689 SCVector3 aj(r(j));
690 e += Zi * charge(j) / ai.dist(aj);
691 }
692 }
693
694 // non_q q terms
695 for (int ii=0; ii < q_atoms_.size(); ii++) {
696 int i = q_atoms_[ii];
697 SCVector3 ai(r(i));
698 double Zi = charge(i);
699
700 for (int jj=0; jj < non_q_atoms_.size(); jj++) {
701 int j = non_q_atoms_[jj];
702 SCVector3 aj(r(j));
703 e += Zi * charge(j) / ai.dist(aj);
704 }
705 }
706
707 if (include_qq_) {
708 // q q terms
709 for (int ii=1; ii < q_atoms_.size(); ii++) {
710 int i = q_atoms_[ii];
711 SCVector3 ai(r(i));
712 double Zi = charge(i);
713
714 for (int jj=0; jj < ii; jj++) {
715 int j = q_atoms_[jj];
716 SCVector3 aj(r(j));
717 e += Zi * charge(j) / ai.dist(aj);
718 }
719 }
720 }
721
722 return e;
723}
724
725void
726Molecule::nuclear_repulsion_1der(int center, double xyz[3])
727{
728 int i,j,k;
729 double rd[3],r2;
730 double factor;
731
732 xyz[0] = 0.0;
733 xyz[1] = 0.0;
734 xyz[2] = 0.0;
735
736 SCVector3 r_center(r(center));
737 double Z_center = charge(center);
738 bool center_is_Q = (atom_symbol(center) == "Q");
739
740 // this handles center = Q or non_Q and atom = non_Q
741 for (int ii=0; ii < non_q_atoms_.size(); ii++) {
742 int i = non_q_atoms_[ii];
743 if (i == center) continue;
744 SCVector3 r_i(r(i));
745
746 r2 = 0.0;
747 for (k=0; k < 3; k++) {
748 rd[k] = r_center[k] - r_i[k];
749 r2 += rd[k]*rd[k];
750 }
751 factor = - Z_center * charge(i) * pow(r2,-1.5);
752 for (k=0; k<3; k++) {
753 xyz[k] += factor * rd[k];
754 }
755 }
756
757 // this handles center = Q or non_Q and atom = Q
758 for (int ii=0; ii < q_atoms_.size(); ii++) {
759 int i = q_atoms_[ii];
760 if (i == center || (!include_qq_ && center_is_Q)) continue;
761 SCVector3 r_i(r(i));
762
763 r2 = 0.0;
764 for (k=0; k < 3; k++) {
765 rd[k] = r_center[k] - r_i[k];
766 r2 += rd[k]*rd[k];
767 }
768 factor = - Z_center * charge(i) * pow(r2,-1.5);
769 for (k=0; k<3; k++) {
770 xyz[k] += factor * rd[k];
771 }
772 }
773}
774
775void
776Molecule::nuclear_charge_efield(const double *charges,
777 const double *position, double *efield)
778{
779 double tmp;
780 double rd[3];
781
782 for (int i=0; i<3; i++) efield[i] = 0.0;
783
784 if (include_q_) {
785 for (int i=0; i<natoms_; i++) {
786 SCVector3 a(r(i));
787 tmp = 0.0;
788 for (int j=0; j<3; j++) {
789 rd[j] = position[j] - a[j];
790 tmp += rd[j]*rd[j];
791 }
792 tmp = charges[i]/(tmp*sqrt(tmp));
793 for (int j=0; j<3; j++) {
794 efield[j] += rd[j] * tmp;
795 }
796 }
797 }
798 else {
799 for (int ii=0; ii<non_q_atoms_.size(); ii++) {
800 int i = non_q_atoms_[ii];
801 SCVector3 a(r(i));
802 tmp = 0.0;
803 for (int j=0; j<3; j++) {
804 rd[j] = position[j] - a[j];
805 tmp += rd[j]*rd[j];
806 }
807 tmp = charges[i]/(tmp*sqrt(tmp));
808 for (int j=0; j<3; j++) {
809 efield[j] += rd[j] * tmp;
810 }
811 }
812 }
813}
814
815void
816Molecule::nuclear_efield(const double *position, double *efield)
817{
818 double tmp;
819 double rd[3];
820
821 for (int i=0; i<3; i++) efield[i] = 0.0;
822
823 if (include_q_) {
824 for (int i=0; i<natoms_; i++) {
825 SCVector3 a(r(i));
826 tmp = 0.0;
827 for (int j=0; j<3; j++) {
828 rd[j] = position[j] - a[j];
829 tmp += rd[j]*rd[j];
830 }
831 tmp = charge(i)/(tmp*sqrt(tmp));
832 for (int j=0; j<3; j++) {
833 efield[j] += rd[j] * tmp;
834 }
835 }
836 }
837 else {
838 for (int ii=0; ii<non_q_atoms_.size(); ii++) {
839 int i = non_q_atoms_[ii];
840 SCVector3 a(r(i));
841 tmp = 0.0;
842 for (int j=0; j<3; j++) {
843 rd[j] = position[j] - a[j];
844 tmp += rd[j]*rd[j];
845 }
846 tmp = charge(i)/(tmp*sqrt(tmp));
847 for (int j=0; j<3; j++) {
848 efield[j] += rd[j] * tmp;
849 }
850 }
851 }
852}
853
854int
855Molecule::atom_at_position(double *v, double tol) const
856{
857 SCVector3 p(v);
858 for (int i=0; i < natom(); i++) {
859 SCVector3 ai(r(i));
860 if (p.dist(ai) < tol) return i;
861 }
862 return -1;
863}
864
865void
866Molecule::symmetrize(const Ref<PointGroup> &pg, double tol)
867{
868 pg_ = new PointGroup(pg);
869
870 // translate to the origin of the symmetry frame
871 double r[3];
872 for (int i=0; i<3; i++) {
873 r[i] = -pg_->origin()[i];
874 pg_->origin()[i] = 0;
875 }
876 translate(r);
877
878 symmetrize(tol);
879}
880
881// We are given a molecule which may or may not have just the symmetry
882// distinct atoms in it. We have to go through the existing set of atoms,
883// perform each symmetry operation in the point group on each of them, and
884// then add the new atom if it isn't in the list already
885
886void
887Molecule::symmetrize(double tol)
888{
889 // if molecule is c1, don't do anything
890 if (!strcmp(this->point_group()->symbol(),"c1")) {
891 init_symmetry_info();
892 return;
893 }
894
895 clear_symmetry_info();
896
897 Molecule *newmol = new Molecule(*this);
898
899 CharacterTable ct = this->point_group()->char_table();
900
901 SCVector3 np;
902 SymmetryOperation so;
903
904 for (int i=0; i < natom(); i++) {
905 SCVector3 ac(r(i));
906
907 for (int g=0; g < ct.order(); g++) {
908 so = ct.symm_operation(g);
909 for (int ii=0; ii < 3; ii++) {
910 np[ii]=0;
911 for (int jj=0; jj < 3; jj++) np[ii] += so(ii,jj) * ac[jj];
912 }
913
914 int atom = newmol->atom_at_position(np.data(), tol);
915 if (atom < 0) {
916 newmol->add_atom(Z_[i],np[0],np[1],np[2],label(i));
917 }
918 else {
919 if (Z(i) != newmol->Z(atom)
920 || fabs(mass(i)-newmol->mass(atom))>1.0e-10) {
921 throw ToleranceExceeded("symmetrize: atom mismatch",
922 __FILE__, __LINE__,
923 1.0e-10, fabs(mass(i)-newmol->mass(atom)),
924 class_desc());
925 }
926 }
927 }
928 }
929
930 Ref<Units> saved_units = geometry_units_;
931 *this = *newmol;
932 geometry_units_ = saved_units;
933 delete newmol;
934
935 init_symmetry_info();
936}
937
938void
939Molecule::translate(const double *r)
940{
941 for (int i=0; i < natom(); i++) {
942 r_[i][0] += r[0];
943 r_[i][1] += r[1];
944 r_[i][2] += r[2];
945 }
946}
947
948// move the molecule to the center of mass
949void
950Molecule::move_to_com()
951{
952 SCVector3 com = -center_of_mass();
953 translate(com.data());
954}
955
956// find the 3 principal coordinate axes, and rotate the molecule to be
957// aligned along them. also rotate the symmetry frame contained in point_group
958void
959Molecule::transform_to_principal_axes(int trans_frame)
960{
961 // mol_move_to_com(mol);
962
963 double *inert[3], inert_dat[9], *evecs[3], evecs_dat[9];
964 double evals[3];
965
966 int i,j,k;
967 for (i=0; i < 3; i++) {
968 inert[i] = &inert_dat[i*3];
969 evecs[i] = &evecs_dat[i*3];
970 }
971 memset(inert_dat,'\0',sizeof(double)*9);
972 memset(evecs_dat,'\0',sizeof(double)*9);
973
974 for (i=0; i < natom(); i++) {
975 SCVector3 ac(r(i));
976 double m=mass(i);
977 inert[0][0] += m * (ac[1]*ac[1] + ac[2]*ac[2]);
978 inert[1][0] -= m * ac[0]*ac[1];
979 inert[1][1] += m * (ac[0]*ac[0] + ac[2]*ac[2]);
980 inert[2][0] -= m * ac[0]*ac[2];
981 inert[2][1] -= m * ac[1]*ac[2];
982 inert[2][2] += m * (ac[0]*ac[0] + ac[1]*ac[1]);
983 }
984
985 inert[0][1] = inert[1][0];
986 inert[0][2] = inert[2][0];
987 inert[1][2] = inert[2][1];
988
989 // cleanup inert
990 for (i=0; i < 3; i++) {
991 for (int j=0; j <= i; j++) {
992 if (fabs(inert[i][j]) < 1.0e-5) {
993 inert[i][j]=inert[j][i]=0.0;
994 }
995 }
996 }
997
998 cmat_diag(inert, evals, evecs, 3, 1, 1e-14);
999
1000 // cleanup evecs
1001 for (i=0; i < 3; i++) {
1002 for (int j=0; j < 3; j++) {
1003 if (fabs(evecs[i][j]) < 1.0e-5) {
1004 evecs[i][j]=0.0;
1005 }
1006 }
1007 }
1008
1009 for (i=0; i<natom(); i++) {
1010 double a[3];
1011 a[0] = r(i,0); a[1] = r(i,1); a[2] = r(i,2);
1012 for (j=0; j<3; j++) {
1013 double e = 0.0;
1014 for (k=0; k<3; k++) {
1015 e += a[k] * evecs[k][j];
1016 }
1017 r_[i][j] = e;
1018 }
1019 }
1020
1021 if (!trans_frame) return;
1022
1023 SymmetryOperation tso=point_group()->symm_frame();
1024
1025 for (i=0; i < 3; i++) {
1026 for (int j=0; j < 3; j++) {
1027 double t=0;
1028 for (int k=0; k < 3; k++) t += tso[k][j]*evecs[k][i];
1029 pg_->symm_frame()[i][j] = t;
1030 }
1031 }
1032}
1033
1034void
1035Molecule::transform_to_symmetry_frame()
1036{
1037 int i,j,k;
1038 double t[3][3];
1039
1040 SymmetryOperation tso=point_group()->symm_frame();
1041
1042 for (i=0; i<3; i++) {
1043 for (j=0; j<3; j++) {
1044 t[i][j] = tso[i][j];
1045 }
1046 }
1047
1048 for (i=0; i<natom(); i++) {
1049 double a[3];
1050 a[0] = r(i,0); a[1] = r(i,1); a[2] = r(i,2);
1051 for (j=0; j<3; j++) {
1052 double e = 0.0;
1053 for (k=0; k<3; k++) {
1054 e += a[k] * t[k][j];
1055 }
1056 r_[i][j] = e;
1057 }
1058 }
1059
1060 for (i=0; i<3; i++) {
1061 for (j=0; j<3; j++) {
1062 double e=0;
1063 for (k=0; k<3; k++) e += tso[k][j]*t[k][i];
1064 pg_->symm_frame()[i][j] = e;
1065 }
1066 }
1067}
1068
1069// given a molecule, make sure that equivalent centers have coordinates
1070// that really map into each other
1071
1072void
1073Molecule::cleanup_molecule(double tol)
1074{
1075 // if symmetry is c1, do nothing else
1076 if (!strcmp(point_group()->symbol(),"c1")) return;
1077
1078 int i;
1079 SCVector3 up,np,ap;
1080 SymmetryOperation so;
1081 CharacterTable ct = point_group()->char_table();
1082
1083 // first clean up the unique atoms by replacing each coordinate with the
1084 // average of coordinates obtained by applying all symmetry operations to
1085 // the original atom, iff the new atom ends up near the original atom
1086 for (i=0; i < nunique(); i++) {
1087 // up will store the original coordinates of unique atom i
1088 up = r(unique(i));
1089 // ap will hold the average coordinate (times the number of coordinates)
1090 // initialize it to the E result
1091 ap = up;
1092 int ncoor = 1;
1093 // loop through all sym ops except E
1094 for (int g=1; g < ct.order(); g++) {
1095 so = ct.symm_operation(g);
1096 for (int ii=0; ii < 3; ii++) {
1097 np[ii]=0;
1098 for (int jj=0; jj < 3; jj++) np[ii] += so(ii,jj) * up[jj];
1099 }
1100 if (np.dist(up) < 0.1) {
1101 for (int jj=0; jj < 3; jj++) ap[jj] += np[jj];
1102 ncoor++;
1103 }
1104 }
1105 // replace the unique coordinate with the average coordinate
1106 r_[unique(i)][0] = ap[0] / ncoor;
1107 r_[unique(i)][1] = ap[1] / ncoor;
1108 r_[unique(i)][2] = ap[2] / ncoor;
1109 }
1110
1111 // find the atoms equivalent to each unique atom and eliminate
1112 // numerical errors that may be in the equivalent atom's coordinates
1113
1114 // loop through unique atoms
1115 for (i=0; i < nunique(); i++) {
1116 // up will store the coordinates of unique atom i
1117 up = r(unique(i));
1118
1119 // loop through all sym ops except E
1120 for (int g=1; g < ct.order(); g++) {
1121 so = ct.symm_operation(g);
1122 for (int ii=0; ii < 3; ii++) {
1123 np[ii]=0;
1124 for (int jj=0; jj < 3; jj++) np[ii] += so(ii,jj) * up[jj];
1125 }
1126
1127 // loop through equivalent atoms
1128 int found = 0;
1129 for (int j=0; j < natom(); j++) {
1130 // see if j is generated from i
1131 if (np.dist(SCVector3(r(j))) < tol) {
1132 r_[j][0] = np[0];
1133 r_[j][1] = np[1];
1134 r_[j][2] = np[2];
1135 found = 1;
1136 }
1137 }
1138 if (!found) {
1139 SCException ex("cleanup: couldn't find atom",
1140 __FILE__, __LINE__, class_desc());
1141 try {
1142 ex.elaborate()
1143 << "couldn't find atom at " << np << endl
1144 << "transforming uniq atom " << i << " at " << up << endl
1145 << "with symmetry op " << g << ":" << endl;
1146 so.print(ex.elaborate());
1147 }
1148 catch (...) {}
1149 throw ex;
1150 }
1151 }
1152 }
1153
1154}
1155
1156///////////////////////////////////////////////////////////////////
1157// Compute the principal axes and the principal moments of inertia
1158///////////////////////////////////////////////////////////////////
1159
1160void
1161Molecule::principal_moments_of_inertia(double *evals, double **evecs) const
1162{
1163
1164 // The principal moments of inertia are computed in amu*angstrom^2
1165 // evals: principal moments of inertia
1166 // evecs: principal axes (optional argument)
1167
1168 Ref<Units> units = new Units("angstroms * angstroms");
1169 double au_to_angs = units->from_atomic_units();
1170
1171 double *inert[3]; // inertia tensor
1172
1173 int i, j;
1174 int delete_evecs = 0;
1175
1176 // (allocate and) initialize evecs, evals, and inert
1177 if (!evecs) {
1178 evecs = new double*[3];
1179 for (i=0; i<3; i++) evecs[i] = new double[3];
1180 delete_evecs = 1;
1181 }
1182 for (i=0; i<3; i++) {
1183 inert[i] = new double[3];
1184 memset(inert[i],'\0',sizeof(double)*3);
1185 memset(evecs[i],'\0',sizeof(double)*3);
1186 }
1187 memset(evals,'\0',sizeof(double)*3);
1188
1189 SCVector3 com = center_of_mass();
1190
1191 // compute inertia tensor
1192 SCVector3 ac;
1193 for (i=0; i<natom(); i++) {
1194 ac = r(i);
1195 // compute moments of inertia wrt center of mass
1196 for (j=0; j<3; j++) ac(j) -= com(j);
1197 double m=au_to_angs*mass(i);
1198 inert[0][0] += m * (ac[1]*ac[1] + ac[2]*ac[2]);
1199 inert[1][0] -= m * ac[0]*ac[1];
1200 inert[1][1] += m * (ac[0]*ac[0] + ac[2]*ac[2]);
1201 inert[2][0] -= m * ac[0]*ac[2];
1202 inert[2][1] -= m * ac[1]*ac[2];
1203 inert[2][2] += m * (ac[0]*ac[0] + ac[1]*ac[1]);
1204 }
1205 inert[0][1] = inert[1][0];
1206 inert[0][2] = inert[2][0];
1207 inert[1][2] = inert[2][1];
1208
1209 cmat_diag(inert, evals, evecs, 3, 1, 1e-14);
1210
1211 if (delete_evecs) {
1212 for (i=0; i<3; i++) delete[] evecs[i];
1213 delete[] evecs;
1214 }
1215 for (i=0; i<3; i++) {
1216 delete[] inert[i];
1217 }
1218}
1219
1220int
1221Molecule::n_core_electrons()
1222{
1223 int i,n=0;
1224 for (i=0; i<natom(); i++) {
1225 if (charge(i) == 0.0) continue;
1226 int z = Z_[i];
1227 if (z > 2) n += 2;
1228 if (z > 10) n += 8;
1229 if (z > 18) n += 8;
1230 if (z > 30) n += 10;
1231 if (z > 36) n += 8;
1232 if (z > 48) n += 10;
1233 if (z > 54) n += 8;
1234 if (z > 72) {
1235 throw LimitExceeded<int>("n_core_electrons: atomic number too large",
1236 __FILE__, __LINE__, 72, z, class_desc());
1237 }
1238 }
1239 return n;
1240}
1241
1242int
1243Molecule::max_z()
1244{
1245 int i, maxz=0;
1246 for (i=0; i<natom(); i++) {
1247 int z = Z_[i];
1248 if (z>maxz) maxz = z;
1249 }
1250 return maxz;
1251}
1252
1253void
1254Molecule::read_pdb(const char *filename)
1255{
1256 clear();
1257 ifstream in(filename);
1258 Ref<Units> units = new Units("angstrom");
1259 while (in.good()) {
1260 const int max_line = 80;
1261 char line[max_line];
1262 in.getline(line,max_line);
1263 char *endofline = (char*) memchr(line, 0, max_line);
1264 if (endofline) memset(endofline, ' ', &line[max_line-1] - endofline);
1265 if (!in.good()) break;
1266 if (strncmp(line,"ATOM ",6) == 0
1267 ||strncmp(line,"HETATM",6) == 0) {
1268
1269 char element[3];
1270 strncpy(element,&line[76],2); element[2] = '\0';
1271 char name[5];
1272 strncpy(name,&line[12],4); name[4] = '\0';
1273 if (element[0]==' '&&element[1]==' ') {
1274 // no element was given so get the element from the atom name
1275 if (name[0]!=' '&&name[3]!=' ') {
1276 // some of the atom label may have been
1277 // pushed over into the element fields
1278 // so check the residue
1279 char resName[4];
1280 strncpy(resName,&line[17],3); resName[3] = '\0';
1281 if (strncmp(line,"ATOM ",6)==0&&(0
1282 ||strcmp(resName,"ALA")==0||strcmp(resName,"A ")==0
1283 ||strcmp(resName,"ARG")==0||strcmp(resName,"R ")==0
1284 ||strcmp(resName,"ASN")==0||strcmp(resName,"N ")==0
1285 ||strcmp(resName,"ASP")==0||strcmp(resName,"D ")==0
1286 ||strcmp(resName,"ASX")==0||strcmp(resName,"B ")==0
1287 ||strcmp(resName,"CYS")==0||strcmp(resName,"C ")==0
1288 ||strcmp(resName,"GLN")==0||strcmp(resName,"Q ")==0
1289 ||strcmp(resName,"GLU")==0||strcmp(resName,"E ")==0
1290 ||strcmp(resName,"GLX")==0||strcmp(resName,"Z ")==0
1291 ||strcmp(resName,"GLY")==0||strcmp(resName,"G ")==0
1292 ||strcmp(resName,"HIS")==0||strcmp(resName,"H ")==0
1293 ||strcmp(resName,"ILE")==0||strcmp(resName,"I ")==0
1294 ||strcmp(resName,"LEU")==0||strcmp(resName,"L ")==0
1295 ||strcmp(resName,"LYS")==0||strcmp(resName,"K ")==0
1296 ||strcmp(resName,"MET")==0||strcmp(resName,"M ")==0
1297 ||strcmp(resName,"PHE")==0||strcmp(resName,"F ")==0
1298 ||strcmp(resName,"PRO")==0||strcmp(resName,"P ")==0
1299 ||strcmp(resName,"SER")==0||strcmp(resName,"S ")==0
1300 ||strcmp(resName,"THR")==0||strcmp(resName,"T ")==0
1301 ||strcmp(resName,"TRP")==0||strcmp(resName,"W ")==0
1302 ||strcmp(resName,"TYR")==0||strcmp(resName,"Y ")==0
1303 ||strcmp(resName,"VAL")==0||strcmp(resName,"V ")==0
1304 ||strcmp(resName,"A ")==0
1305 ||strcmp(resName,"+A ")==0
1306 ||strcmp(resName,"C ")==0
1307 ||strcmp(resName,"+C ")==0
1308 ||strcmp(resName,"G ")==0
1309 ||strcmp(resName,"+G ")==0
1310 ||strcmp(resName,"I ")==0
1311 ||strcmp(resName,"+I ")==0
1312 ||strcmp(resName,"T ")==0
1313 ||strcmp(resName,"+T ")==0
1314 ||strcmp(resName,"U ")==0
1315 ||strcmp(resName,"+U ")==0
1316 )) {
1317 // there no two letter elements for these cases
1318 element[0] = name[0];
1319 element[1] = '\0';
1320 }
1321 }
1322 else {
1323 strncpy(element,name,2); element[2] = '\0';
1324 }
1325 }
1326 if (element[0] == ' ') {
1327 element[0] = element[1];
1328 element[1] = '\0';
1329 }
1330
1331 int Z = atominfo_->string_to_Z(element);
1332
1333 char field[9];
1334 strncpy(field,&line[30],8); field[8] = '\0';
1335 double x = atof(field);
1336 strncpy(field,&line[38],8); field[8] = '\0';
1337 double y = atof(field);
1338 strncpy(field,&line[46],8); field[8] = '\0';
1339 double z = atof(field);
1340 add_atom(Z,
1341 x*units->to_atomic_units(),
1342 y*units->to_atomic_units(),
1343 z*units->to_atomic_units());
1344 }
1345 else {
1346 // skip to next record
1347 }
1348 }
1349}
1350
1351void
1352Molecule::print_pdb(ostream& os, char *title) const
1353{
1354 Ref<Units> u = new Units("angstrom");
1355 double bohr = u->from_atomic_units();
1356
1357 if (title)
1358 os << scprintf("%-10s%-60s\n","COMPND",title);
1359 else
1360 os << scprintf("%-10s%-60s\n","COMPND","Title");
1361
1362 if (title)
1363 os << scprintf("REMARK %s\n", title);
1364
1365 int i;
1366 for (i=0; i < natom(); i++) {
1367 char symb[4];
1368 std::string symbol(atom_symbol(i));
1369 sprintf(symb,"%s1",symbol.c_str());
1370
1371 os << scprintf(
1372 "HETATM%5d %-3s UNK %5d %8.3f%8.3f%8.3f 0.00 0.00 0\n",
1373 i+1, symb, 0, r(i,0)*bohr, r(i,1)*bohr, r(i,2)*bohr);
1374 }
1375
1376 for (i=0; i < natom(); i++) {
1377 double at_rad_i = atominfo_->atomic_radius(Z_[i]);
1378 SCVector3 ai(r(i));
1379
1380 os << scprintf("CONECT%5d",i+1);
1381
1382 for (int j=0; j < natom(); j++) {
1383
1384 if (j==i) continue;
1385
1386 double at_rad_j = atominfo_->atomic_radius(Z_[j]);
1387 SCVector3 aj(r(j));
1388
1389 if (ai.dist(aj) < 1.1*(at_rad_i+at_rad_j))
1390 os << scprintf("%5d",j+1);
1391 }
1392
1393 os << endl;
1394 }
1395
1396 os << "END" << endl;
1397 os.flush();
1398}
1399
1400double
1401Molecule::mass(int atom) const
1402{
1403 if (!mass_ || mass_[atom] == 0) {
1404 return atominfo_->mass(Z_[atom]);
1405 }
1406 return mass_[atom];
1407}
1408
1409const char *
1410Molecule::label(int atom) const
1411{
1412 if (!labels_) return 0;
1413 return labels_[atom];
1414}
1415
1416std::string
1417Molecule::atom_name(int iatom) const
1418{
1419 return atominfo_->name(Z_[iatom]);
1420}
1421
1422std::string
1423Molecule::atom_symbol(int iatom) const
1424{
1425 return atominfo_->symbol(Z_[iatom]);
1426}
1427
1428/////////////////////////////////////////////////////////////////////////////
1429
1430// Local Variables:
1431// mode: c++
1432// c-file-style: "CLJ"
1433// End:
Note: See TracBrowser for help on using the repository browser.