1<?php
2/********************************************************************
3baphpel - PHP interface to OpenBabel
4Copyright (C) 2013  Maciej Wojcikowski <maciek@wojcikowski.pl>
5
6This file is part of  of the Open Babel project.
7
8This file is part of the Open Babel project.
9For more information, see <http://openbabel.org/>
10
11This program is free software; you can redistribute it and/or modify
12it under the terms of the GNU General Public License as published by
13the Free Software Foundation version 2 of the License.
14
15This program is distributed in the hope that it will be useful,
16but WITHOUT ANY WARRANTY; without even the implied warranty of
17MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18GNU General Public License for more details.
19
20********************************************************************/
21namespace baphpel;
22
23include_once 'openbabel.php';
24
25# define global elements
26$_builder = new \OBBuilder;
27$_obconv = new \OBConversion;
28
29function vector2array($v) {
30	$out = array();
31	$num = $v -> size();
32	for($i=0;$i<$num;$i++) {
33		$out[] = $v -> get($i);
34	}
35	return $out;
36}
37
38function _formatstodict($list) {
39	global $_obconv;
40	$list = vector2array($_obconv -> $list());
41	foreach($list as $f) {
42		$fmt = explode(' -- ', preg_replace(array('/\[Read-only\]/'), '/\[Write-only\]/', $f));
43		$out[trim($fmt[0])] = trim($fmt[1]);
44	}
45	return $out;
46}
47
48$informats = _formatstodict('GetSupportedInputFormat');
49$outformats = _formatstodict('GetSupportedOutputFormat');
50
51function _getplugins($findplugins, $names) {
52	$find = explode('::', $findplugins);
53	foreach($names as $x) {
54		if(in_array($find[0], array('OBDescriptor', 'OBForceField'))) {
55			$f = $find[0]::$find[1]($x);
56		}
57		else {
58			$f1 = new $find[0](null);
59			$f = $f1 -> $find[1]($x);
60		}
61		if($f) {
62			$plugins[$x] = $f;
63		}
64	}
65	return $plugins;
66}
67
68function _getpluginnames($ptype) {
69	$plugins = new \vectorString;
70	\OBPlugin::ListAsVector($ptype, null, $plugins);
71	$plugins = vector2array($plugins);
72	foreach($plugins as $p) {
73		$out[] = strtolower(explode(' ', trim($p))[0]);
74	}
75	return $out;
76}
77
78
79/* A list of supported descriptors */
80$descs = _getpluginnames('descriptors');
81$_descdict = _getplugins('\OBDescriptor::FindType', $descs);
82
83/* A list of supported forcefields */
84$forcefields = _getpluginnames('forcefields');
85$_forcefields = _getplugins('\OBForceField::FindForceField', $forcefields);
86
87/* A list of supported fingerprint types */
88$fps = _getpluginnames('fingerprints');
89$_fingerprinters = _getplugins('\OBFingerprint::FindType', $fps);
90
91/* A list of supported operations */
92$operations = _getpluginnames('ops');
93$_operations = _getplugins('\OBOp::FindType', $operations);
94
95function readfile($format, $filename, $opt=array()) {
96	# setup converter
97	$OBConversion = new \OBConversion;
98	$OBConversion -> SetInFormat($format);
99	# set options
100	if(!empty($opt)) {
101		foreach($opt as $k => $v) {
102			if(!empty($v)) {
103				$OBConversion -> AddOption($k, $OBConversion::INOPTIONS, $v);
104			}
105			else {
106				$OBConversion -> AddOption($k, $OBConversion::INOPTIONS);
107			}
108		}
109	}
110
111	class filereader implements \Iterator {
112		private $position = 0;
113		private $notatend = null;
114		private $OBMol = null;
115
116		public function __construct($OBConversion, $filename) {
117			$this -> OBConversion = $OBConversion;
118			$this -> OBMol = new \OBMol;
119			$this -> notatend = $this -> OBConversion -> ReadFile($this -> OBMol, $filename);
120		}
121
122		function rewind() {
123			$this->position = 0;
124		}
125
126		function current() {
127			return new Molecule($this -> OBMol);
128		}
129
130		function key() {
131			return $this->position;
132		}
133
134		function next() {
135			$this -> position++;
136			$this -> OBMol = new \OBMol;
137			$this -> notatend = $this -> OBConversion -> Read($this -> OBMol);
138		}
139
140		function valid() {
141			return $this -> notatend;
142		}
143	}
144
145	return new filereader($OBConversion, $filename);
146}
147
148function readstring($format, $string, $opt=array()) {
149	# setup converter
150	$OBConversion = new \OBConversion;
151	$OBConversion -> SetInFormat($format);
152	# set options
153	if(!empty($opt)) {
154		foreach($opt as $k => $v) {
155			if(!empty($v)) {
156				$OBConversion -> AddOption($k, $OBConversion::INOPTIONS, $v);
157			}
158			else {
159				$OBConversion -> AddOption($k, $OBConversion::INOPTIONS);
160			}
161		}
162	}
163
164	$OBMol = new \OBMol;
165	$OBConversion->ReadString($OBMol, $string);
166	return new Molecule($OBMol);
167}
168
169class Outputfile {
170	public function __construct($format, $filename, $overwrite=False, $opt=array()) {
171		$this -> format = $format;
172		$this -> filename = $filename;
173		if(!$overwrite && file_exists(self.filename)) {
174			throw new \Exception($this -> filename." already exists. Use 'overwrite=True' to overwrite it.");
175		}
176		$this -> OBConversion = new \OBConversion;
177		$formatok = $this -> OBConversion -> SetOutFormat($this -> format);
178		if(!$formatok) {
179			throw new \Exception($this -> format." is not a recognised Open Babel format");
180		}
181		foreach($opt as $key => $value) {
182			if($v === null) {
183				$this -> OBConversion -> AddOption($k, $this -> OBConversion -> OUTOPTIONS);
184			}
185			else {
186				$this -> OBConversion -> AddOption($k, $this -> OBConversion -> OUTOPTIONS, $v);
187			}
188		}
189		$this -> total = 0; # The total number of molecules written to the file
190	}
191
192	public function write($molecule) {
193		if(!$this -> filename) {
194			throw new \Exception("Outputfile instance is closed.");
195		}
196
197		if($this -> total == 0) {
198			$this -> OBConversion -> WriteFile($molecule -> OBMol, $this -> filename);
199		}
200		else {
201			$this -> OBConversion -> Write($molecule -> OBMol);
202		}
203		$this -> total += 1;
204	}
205
206	public function close() {
207		$this -> OBConversion -> CloseOutFile();
208		$this -> filename = null;
209	}
210}
211
212class Molecule implements \Iterator {
213	public $OBMol = null;
214	private $position = 0;
215	private $atoms = array();
216	private $residues = array();
217
218	public function __construct($OBMol) {
219		$this -> OBMol = $OBMol;
220	}
221
222	public function __get($name) {
223		switch($name) {
224			case 'atoms':
225				if(empty($this -> atoms)) { # generate atoms only once
226					$out = array();
227					$num = $this -> OBMol -> NumAtoms();
228					for($i=1;$i<=$num;$i++) {
229						$atom = $this -> OBMol -> GetAtom($i);
230						if(empty($atom)) {
231							echo $i.'!!!';
232						}
233						$out[] = new Atom($this -> OBMol -> GetAtom($i));
234					}
235					$this -> atoms = $out;
236				}
237				else {
238					$out = $this -> atoms;
239				}
240			break;
241			case 'residues':
242				if(empty($this -> residues)) { # generate residues only once
243					$out = array();
244					$num = $this -> OBMol -> NumResidues();
245					for($i=0;$i<$num;$i++) {
246						$out[] = new Residue($this -> OBMol -> GetResidue($i));
247					}
248					$this -> residues = $out;
249				}
250				else {
251					$out = $this -> residues;
252				}
253			break;
254			case 'charge':
255				$out = $this -> OBMol -> GetTotalCharge();
256			break;
257			case 'conformers':
258				$out = $this -> OBMol -> GetConformers();
259			break;
260			case 'data':
261				$out = new MoleculeData($this -> OBMol);
262			break;
263			case 'dim':
264				$out = $this -> OBMol -> GetDimension();
265			break;
266			case 'energy':
267				$out = $this -> OBMol -> GetEnergy();
268			break;
269			case 'exactmass':
270				$out = $this -> OBMol -> GetExactMass();
271			break;
272			case 'formula':
273				$out = $this -> OBMol -> GetFormula();
274			break;
275			case 'molwt':
276				$out = $this -> OBMol -> GetMolWt();
277			break;
278			case 'spin':
279				$out = $this -> OBMol -> GetTotalSpinMultiplicity();
280			break;
281			case 'sssr':
282				$out = vector2array($this -> OBMol -> GetSSSR());
283			break;
284			case 'title':
285				$out = $this -> OBMol -> GetTitle();
286			break;
287#			case 'unitcell':
288#
289#			break;
290			case '_exchange':
291				if($this -> OBMol -> HasNonZeroCoords()) {
292					return $this -> write('mol');
293				}
294				else {
295					return preg_split('/[\t\s]+/', trim($this -> write('can')))[0];
296				}
297			break;
298		}
299		return $out;
300	}
301
302	public function __set($name, $value) {
303		switch($name) {
304			case 'title':
305				$out = $this -> OBMol -> SetTitle($value);
306			break;
307		}
308		return $out;
309	}
310
311	# iterator part
312	public function rewind() {
313		$this->position = 0;
314		# generate atoms
315		$this-> __get('atoms');
316	}
317
318	public function current() {
319		return $this->atoms[$this->position];
320	}
321
322	public function key() {
323		return $this->position;
324	}
325
326	public function next() {
327		$this->position++;
328	}
329
330	public function valid() {
331		return isset($this->atoms[$this->position]);
332	}
333	# end iterator
334
335	public function calcdesc($descnames = array()) {
336		global $descs, $_descdict;
337		if(empty($descnames)) {
338			$descnames = $descs;
339		}
340		foreach($descnames as $descname) {
341			if(isset($_descdict[$descname])) {
342				$out[$descname] = $_descdict[$descname] -> Predict($this -> OBMol);
343			}
344			else {
345				throw new \Exception($descname.' is not recognised Open Babel descriptor type');
346			}
347		}
348		return $out;
349	}
350
351	public function calcfp($fptype="FP2") {
352		global $_fingerprinters;
353		$fp = new \vectorUnsignedInt;
354		$fptype = strtolower($fptype);
355		if(isset($_fingerprinters[$fptype])) {
356			$fingerprinter = $_fingerprinters[$fptype];
357			$fingerprinter -> GetFingerprint($this -> OBMol, $fp);
358			return new Fingerprint($fp);
359		}
360		else {
361			throw new \Exception($fptype.' is not a recognised Open Babel Fingerprint type');
362		}
363	}
364
365	public function __toString() {
366		return $this -> write();
367	}
368
369	public function write($format = 'smi', $filename = null, $overwrite = false, $opt = array()) {
370		$OBConversion = new \OBConversion;
371		$OBConversion -> SetOutFormat($format);
372		# set options
373		if(!empty($opt)) {
374			foreach($opt as $k => $v) {
375				if($v === null) {
376					$OBConversion -> AddOption($k, $OBConversion::OUTOPTIONS, $v);
377				}
378				else {
379					$OBConversion -> AddOption($k, $OBConversion::OUTOPTIONS);
380				}
381			}
382		}
383
384		if(!empty($filename)) {
385			if(!file_exists($filename) || file_exists($filename) && $overwrite) {
386				return $OBConversion->WriteFile($this -> OBMol, $filename);
387			}
388		}
389		else {
390			return $OBConversion->WriteString($this -> OBMol);
391		}
392	}
393
394	public function localopt($forcefield = 'mmff94', $steps = 50) {
395		global $_forcefields;
396		$forcefield = strtolower($forcefield);
397		if($this -> dim != 3) {
398			$this -> make3D($forcefield);
399		}
400		$ff = $_forcefields[$forcefield];
401		if(!$ff -> Setup($this -> OBMol)) {
402			return false;
403		}
404		$ff -> SteepestDescent($steps);
405		$ff -> GetCoordinates($this -> OBMol);
406	}
407
408	public function make3D($forcefield = 'mmff94', $steps = 50) {
409		global $_builder;
410		$forcefield = strtolower($forcefield);
411		$_builder -> Build($this -> OBMol);
412		$this -> addh();
413		$this -> localopt($forcefield, $steps);
414	}
415
416	public function addh() {
417		$this -> OBMol -> AddHydrogens();
418	}
419
420	public function removeh() {
421		$this -> OBMol -> DeleteHydrogens();
422	}
423
424	public function convertdbonds() {
425		$this -> OBMol -> ConvertDativeBonds();
426	}
427
428	public function draw() {
429		return $this -> write('svg');
430	}
431}
432
433class Atom {
434	public function __construct($OBAtom) {
435		$this -> OBAtom = $OBAtom;
436	}
437
438	public function __get($name) {
439		switch($name) {
440			case 'coords':
441				$out = array($this -> OBAtom -> GetX(), $this -> OBAtom -> GetY(), $this -> OBAtom -> GetZ());
442			break;
443			case 'atomicmass':
444				$out = $this -> OBAtom -> GetAtomicMass();
445			break;
446			case 'atomicnum':
447				$out = $this -> OBAtom -> GetAtomicNum();
448			break;
449			case 'cidx':
450				$out = $this -> OBAtom -> GetCIdx();
451			break;
452			case 'coordidx':
453				$out = $this -> OBAtom -> GetCoordinateIdx();
454			break;
455			case 'exactmass':
456				$out = $this -> OBAtom -> GetExactMass();
457			break;
458			case 'formalcharge':
459				$out = $this -> OBAtom -> GetFormalCharge();
460			break;
461			case 'heavyvalence':
462				$out = $this -> OBAtom -> GetHvyValence();
463			break;
464			case 'heterovalence':
465				$out = $this -> OBAtom -> GetHeteroValence();
466			break;
467			case 'hyb':
468				$out = $this -> OBAtom -> GetHyb();
469			break;
470			case 'idx':
471				$out = $this -> OBAtom -> GetIdx();
472			break;
473			case 'implicitvalence':
474				$out = $this -> OBAtom -> GetImplicitValence();
475			break;
476			case 'isotope':
477				$out = $this -> OBAtom -> GetIsotope();
478			break;
479			case 'partialcharge':
480				$out = $this -> OBAtom -> GetPartialCharge();
481			break;
482			case 'residue':
483				$out = new Residue($this -> OBAtom -> GetResidue());
484			break;
485			case 'spin':
486				$out = $this -> OBAtom -> GetSpinMultiplicity();
487			break;
488			case 'type':
489				$out = $this -> OBAtom -> GetType();
490			break;
491			case 'valence':
492				$out = $this -> OBAtom -> GetValence();
493			break;
494			case 'vector':
495				$out = $this -> OBAtom -> GetVector();
496			break;
497		}
498		return $out;
499	}
500
501	public function __toString() {
502
503		return 'Atom: '.$this -> atomicnum.' ('.$this -> coords[0] .', '.$this -> coords[1] .', '.$this -> coords[2] .')';
504	}
505}
506
507class Residue {
508	private $atoms = array();
509
510	public function __construct($OBResidue) {
511		$this -> OBResidue = $OBResidue;
512	}
513
514	public function __get($name) {
515		switch($name) {
516			case 'idx':
517				$out = $this -> OBResidue -> GetIdx();
518			break;
519			case 'num':
520				$out = $this -> OBResidue -> GetNum();
521			break;
522			case 'name':
523				$out = $this -> OBResidue -> GetName();
524			break;
525			case 'atoms':
526				if(empty($this -> atoms)) { # generate atoms only once
527					$num = $this -> OBResidue -> GetNumAtoms();
528					$iter = $this -> OBResidue -> BeginAtoms();
529					$out[] = new Atom($this -> OBResidue -> BeginAtom($iter));
530					for($i=0;$i<$num-1;$i++) {
531						$out[] = new Atom($this -> OBResidue -> NextAtom($iter));
532					}
533					$this -> atoms = $out;
534				}
535				else {
536					$out = $this -> atoms;
537				}
538			break;
539		}
540		return $out;
541	}
542}
543
544function tanimoto($fp1, $fp2) {
545	return \OBFingerprint::Tanimoto($fp1 -> fp, $fp2 -> fp);
546}
547
548class Fingerprint {
549	public $fp = null;
550
551	public function __construct($fp) {
552		$this -> fp = $fp;
553	}
554
555	public function __toString() {
556		return implode(', ', vector2array($this -> fp));
557	}
558}
559
560class Smarts {
561	private $obsmarts = null;
562
563	public function __construct($smartspattern) {
564		$this -> obsmarts = new \OBSmartsPattern;
565		if(!$this -> obsmarts -> Init($smartspattern)) {
566			throw new Exception('Invalid SMARTS pattern');
567		}
568	}
569
570	public function findall($mol) {
571		$this -> obsmarts -> Match($mol -> OBMol);
572		foreach(vector2array($this -> obsmarts -> GetUMapList()) as $match) {
573			$out[] = vector2array($match);
574		}
575		return $out;
576	}
577}
578
579class MoleculeData {
580	private $_mol = null;
581
582	public function __construct($obmol) {
583		$this -> _mol = $obmol;
584	}
585
586	public function _data() {
587		$data = vector2array($this -> _mol -> GetData());
588		foreach($data as $d) {
589			if(in_array($d -> GetDataType(), array(\openbabel::PairData, \openbabel::CommentData))) {
590				$out[] = \openbabel::toPairData($d);
591
592			}
593		}
594		return $out;
595	}
596
597	public function keys() {
598		foreach($this -> _data() as $d) {
599			$out[] = $d -> GetAttribute();
600		}
601		return $out;
602	}
603
604	public function values() {
605		foreach($this -> _data() as $d) {
606			$out[] = $d -> GetValue();
607		}
608		return $out;
609	}
610
611	public function items() {
612		foreach($this -> _data() as $d) {
613			$out[$d -> GetAttribute()] = $d -> GetValue();
614		}
615		return $out;
616	}
617}
618?>
619