All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xmlcml.cml.tools.AtomTool Maven / Gradle / Ivy

/* Copyright 2011 Peter Murray-Rust et. al.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.xmlcml.cml.tools;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.log4j.Logger;
import org.xmlcml.cml.base.CMLElement.CoordinateType;
import org.xmlcml.cml.element.CMLAtom;
import org.xmlcml.cml.element.CMLAtomSet;
import org.xmlcml.cml.element.CMLBond;
import org.xmlcml.cml.element.CMLBondStereo;
import org.xmlcml.cml.element.CMLMolecule;
import org.xmlcml.cml.element.CMLMolecule.HydrogenControl;
import org.xmlcml.euclid.Angle;
import org.xmlcml.euclid.Angle.Range;
import org.xmlcml.euclid.EuclidRuntimeException;
import org.xmlcml.euclid.Point3;
import org.xmlcml.euclid.Real2;
import org.xmlcml.euclid.Transform2;
import org.xmlcml.euclid.Vector2;
import org.xmlcml.euclid.Vector3;
import org.xmlcml.molutil.ChemicalElement;
import org.xmlcml.molutil.Molutils;
import org.xmlcml.molutil.ChemicalElement.AS;

import nu.xom.Attribute;

/**
 * Additional tools for atom. Not fully developed.
 * 
 * @author pmr
 */
public class AtomTool {

	static final Logger LOG = Logger.getLogger(AtomTool.class.getName());

	/**
	 * Adds or delete hydrogen atoms to satisfy valence.
	 * 

* Ignore if hydrogenCount attribute is set. *

* Uses algorithm: nH = 8 - group - sumbondorder + formalCharge, where group * is 0-8 in first two rows * * @param atom * @param control specifies whether H are explicit or in hydrogenCount */ public static void adjustHydrogenCountsToValency(CMLAtom atom, CMLMolecule molecule, HydrogenControl control) { if (atom.getHydrogenCountAttribute() == null) { int group = getHydrogenValencyGroup(atom); // these states cannot have hydrogen if (group == -1) { return; } else if (group == -2) { return; } // hydrogen and metals if (group < 4) { return; } int sumBo = getSumNonHydrogenBondOrder(atom); int fc = (atom.getFormalChargeAttribute() == null ? 0 : atom.getFormalCharge()); int nh = 8 - group - sumBo + fc; // non-octet species if (group == 4 && fc == 1) { nh -= 2; } // negative counts are meaningless if (nh < 0) { nh = 0; } atom.setHydrogenCount(nh); } expandImplicitHydrogens(atom, molecule, control); } static String[] elems = {AS.H.value, AS.C.value, AS.N.value, AS.O.value, AS.F.value, AS.Si.value, AS.P.value, AS.S.value, AS.Cl.value, AS.Br.value, AS.I.value}; static int[] group = { 1, 4, 5, 6, 7, 4, 5, 6, 7, 7, 7}; static int[] eneg0 = { 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1}; static int[] eneg1 = { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1}; /** a simple lookup for common atoms. * * examples are C, N, O, F, Si, P, S, Cl, Br, I * if atom has electronegative ligands, (O, F, Cl...) returns -1 * */ public static int getHydrogenValencyGroup(CMLAtom atom) { int elNum = -1; try { String elType = atom.getElementType(); elNum = getElemNumb(elType); if (elNum == -1) { return -1; } if (eneg0[elNum] == 0) { return group[elNum]; } List ligands = atom.getLigandAtoms(); // if atom is susceptible to enegative ligands, exit if they are present for (CMLAtom ligand : ligands) { int ligElNum = getElemNumb(ligand.getElementType()); if (ligElNum == -1 || eneg1[ligElNum] == 1) { return -2; } } } catch (Exception e) { LOG.error("BUG "+e); } int g = (elNum == -1) ? -1 : group[elNum]; return g; } private static int getElemNumb(String elemType) { for (int i = 0; i < elems.length; i++) { if (elems[i].equals(elemType)) { return i; } } return -1; } /** * Sums the formal orders of all bonds from atom to non-hydrogen ligands. *

* Uses 1, 2, 3, A orders and creates the nearest integer. Thus 2 aromatic * bonds sum to 3 and 3 sum to 4. Bonds without order are assumed to be * single. * * @exception RuntimeException null atom in argument * @return Sum of bond orders; may be 0 for isolated atom or atom with only * H ligands */ public static int getSumNonHydrogenBondOrder(CMLAtom atom) throws RuntimeException { float sumBo = 0.0f; List ligandList = atom.getLigandAtoms(); List ligandBondList = atom.getLigandBonds(); for (int i = 0; i < ligandList.size(); i++) { CMLAtom ligand = ligandList.get(i); if (AS.H.equals(ligand.getElementType())) { continue; } CMLBond bond = ligandBondList.get(i); String bo = bond.getOrder(); if (bo != null) { if (CMLBond.isSingle(bo)) { sumBo += 1.0; } if (CMLBond.isDouble(bo)) { sumBo += 2.0; } if (CMLBond.isTriple(bo)) { sumBo += 3.0; } if (bo.equals(CMLBond.AROMATIC)) { sumBo += 1.4; } } else { // if no bond order, assume single sumBo += 1.0; } } return Math.round(sumBo); } /** * Expands implicit hydrogen atoms. *

* TODO this needs looking at. *

* CMLMolecule.NO_EXPLICIT_HYDROGENS *

* CMLMolecule.USE_HYDROGEN_COUNT // no * action * * @param atom * @param control * @throws RuntimeException */ public static void expandImplicitHydrogens(CMLAtom atom, CMLMolecule molecule, HydrogenControl control) throws RuntimeException { if (HydrogenControl.USE_HYDROGEN_COUNT.equals(control)) { return; } if (atom.getHydrogenCountAttribute() == null || atom.getHydrogenCount() == 0) { return; } int hydrogenCount = atom.getHydrogenCount(); int currentHCount = 0; List ligandList = atom.getLigandAtoms(); for (CMLAtom ligand : ligandList) { if (ligand.getElementType().equals(AS.H.value)) { currentHCount++; } } //FIXME this might need rethinking; may not correctly handle sulfur stereochemistry or wiggly bonds if (HydrogenControl.NO_EXPLICIT_HYDROGENS.equals(control) && currentHCount != 0) { return; } String id = atom.getId(); List bonds = atom.getLigandBonds(); /*boolean hasHatch = false; boolean hasWedge = false; boolean hasDouble = false;*/ Angle wedgeAngle = null; Angle hatchAngle = null; double bondLength = 0; List otherAngles = new ArrayList(); for (CMLBond bond : bonds) { if (bond.getBondStereo() != null && bond.getBondStereo().getValue().equals(CMLBondStereo.WEDGE) && bond.getAtom(0) == atom) { //hasWedge = true; wedgeAngle = new Angle(Math.atan2(bond.getAtom(1).getY2() - atom.getY2(), bond.getAtom(1).getX2() - atom.getX2()), org.xmlcml.euclid.Angle.Units.RADIANS); bondLength = (bond.getAtom(1).getY2() - atom.getY2()) * (bond.getAtom(1).getY2() - atom.getY2()) + (bond.getAtom(1).getX2() - atom.getX2()) * (bond.getAtom(1).getX2() - atom.getX2()); } else if (bond.getBondStereo() != null && bond.getBondStereo().getValue().equals(CMLBondStereo.HATCH) && bond.getAtom(0) == atom) { //hasHatch = true; hatchAngle = new Angle(Math.atan2(bond.getAtom(1).getY2() - atom.getY2(), bond.getAtom(1).getX2() - atom.getX2()), org.xmlcml.euclid.Angle.Units.RADIANS); bondLength = (bond.getAtom(1).getY2() - atom.getY2()) * (bond.getAtom(1).getY2() - atom.getY2()) + (bond.getAtom(1).getX2() - atom.getX2()) * (bond.getAtom(1).getX2() - atom.getX2()); }/* else if (bond.getOrder().equals(CMLBond.DOUBLE_D)) { hasDouble = true; }*/ else if (bond.getAtom(0) == atom) { otherAngles.add(new Angle(Math.atan2(bond.getAtom(1).getY2() - atom.getY2(), bond.getAtom(1).getX2() - atom.getX2()), org.xmlcml.euclid.Angle.Units.RADIANS)); } else { otherAngles.add(new Angle(Math.atan2(bond.getAtom(0).getY2() - atom.getY2(), bond.getAtom(0).getX2() - atom.getX2()), org.xmlcml.euclid.Angle.Units.RADIANS)); } } bondLength = Math.sqrt(bondLength); for (int i = 0; i < hydrogenCount - currentHCount; i++) { CMLAtom hatom = new CMLAtom(id + "_h" + (i + 1)); molecule.addAtom(hatom); hatom.setElementType(AS.H.value); //hatom.setXY2(atom.getXY2().plus(new Real2(1, 1))); CMLBond bond = new CMLBond(atom, hatom); molecule.addBond(bond); bond.setOrder(CMLBond.SINGLE_S); if (hatchAngle == null ^ wedgeAngle == null) { CMLBondStereo s = new CMLBondStereo(); s.setXMLContent(hatchAngle != null ? CMLBondStereo.WEDGE : CMLBondStereo.HATCH); bond.setBondStereo(s); Angle baseBondAngle = (hatchAngle != null ? hatchAngle : (wedgeAngle != null ? wedgeAngle : otherAngles.get(2))); Angle diff1 = baseBondAngle.subtract(otherAngles.get(0)); Angle diff2 = baseBondAngle.subtract(otherAngles.get(1)); diff1.setRange(Range.UNLIMITED); diff2.setRange(Range.UNLIMITED); Angle newAngle = (Math.abs(diff1.getRadian()) < Math.abs(diff2.getRadian()) ? otherAngles.get(0).plus(baseBondAngle).multiplyBy(0.5) : otherAngles.get(1).plus(baseBondAngle).multiplyBy(0.5)); positionAtomRelatively(atom, hatom, newAngle, bondLength); } else if (hatchAngle == null && wedgeAngle == null) { if (atom.getXY2() != null) { hatom.setXY2(atom.getXY2().plus(new Real2(1, 1))); } } else if (hatchAngle != null && wedgeAngle != null) { otherAngles.add(wedgeAngle); otherAngles.add(hatchAngle); Collections.sort(otherAngles, new Comparator(){ public int compare(Angle o1, Angle o2) { return (o1.lessThan(o2) ? -1 : 1); } }); Angle largestAngle = new Angle(0); Angle newAngle = null; for (int angle = 0; angle < otherAngles.size() - 1; angle++) { if ((otherAngles.get(angle) == wedgeAngle && otherAngles.get(angle + 1) == hatchAngle) || (otherAngles.get(angle) == hatchAngle && otherAngles.get(angle + 1) == wedgeAngle)) { continue; } Angle diff = otherAngles.get(angle + 1).subtract(otherAngles.get(angle)); if (diff.greaterThan(largestAngle)) { largestAngle = diff; newAngle = otherAngles.get(angle).plus(otherAngles.get(angle + 1)).multiplyBy(0.5); } } Angle diff = otherAngles.get(0).subtract(otherAngles.get(otherAngles.size() - 1)).plus(new Angle(360, org.xmlcml.euclid.Angle.Units.DEGREES)); if (diff.greaterThan(largestAngle)) { largestAngle = diff; newAngle = otherAngles.get(otherAngles.size() - 1).plus(otherAngles.get(0)).multiplyBy(0.5).plus(new Angle(180, org.xmlcml.euclid.Angle.Units.DEGREES)); } positionAtomRelatively(atom, hatom, newAngle, bondLength); } } } private static void positionAtomRelatively(CMLAtom atom, CMLAtom hatom, Angle newAngle, double bondLength) { double xDiff; double yDiff; if (newAngle.isEqualTo(Math.PI, 0.0000001)) { xDiff = -bondLength; yDiff = 0; } else if (newAngle.isEqualTo(Math.PI / 2, 0.0000001)) { xDiff = 0; yDiff = bondLength; } else if (newAngle.isEqualTo(0, 0.0000001)) { xDiff = bondLength; yDiff = 0; } else if (newAngle.isEqualTo(-Math.PI / 2, 0.0000001)) { xDiff = 0; yDiff = -bondLength; } else if (newAngle.isEqualTo(-Math.PI, 0.0000001)) { xDiff = -bondLength; yDiff = 0; } else { double ratio = Math.tan(newAngle.getRadian()); xDiff = Math.sqrt((bondLength * bondLength) / (1 + ratio * ratio)); if (newAngle.greaterThan(Math.PI / 2) && newAngle.lessThan((3 * Math.PI) / 2)) { xDiff *= -1; } yDiff = xDiff * ratio; } hatom.setXY2(atom.getXY2().plus(new Real2(xDiff, yDiff))); } // ---------------------to be looked at:----------------------------------------- // these functions seem to deal mostly with chirality (& associated) // there also appears to be a lot of duplicate functionality /** * add calculated coordinates for hydrogens. * * @param atom * @param type * @param bondLength */ public void addCalculatedCoordinatesForHydrogens(CMLAtom atom, CoordinateType type, double bondLength) { if (CoordinateType.TWOD.equals(type)) { calculateAndAddHydrogenCoordinates(atom, bondLength); } else if (CoordinateType.CARTESIAN.equals(type)) { throw new RuntimeException("CARTESIAN H coords nyi"); } else { throw new RuntimeException("THREED H coords nyi"); } } /** * * @param bondLength */ public static void calculateAndAddHydrogenCoordinates(CMLAtom atom, double bondLength) { List ligandHydrogenList = atom.getLigandHydrogenAtoms(); List ligandList = atom.getLigandAtoms(); List nonHydrogenLigandHydrogenList = new ArrayList(); for (CMLAtom ligand : ligandList) { if (!AS.H.equals(ligand.getElementType())) { nonHydrogenLigandHydrogenList.add(ligand); } } List vectorList = new ArrayList(); try { vectorList = addCoords(atom, nonHydrogenLigandHydrogenList, ligandHydrogenList, bondLength); } catch (Exception e) { LOG.error("Cannot add Hydrogen ", e); } if (vectorList.size() == 0) { } else if (vectorList.size() != ligandHydrogenList.size()) { LOG.error("vectorList ("+vectorList.size()+") != ligandHydrogenList ("+ligandHydrogenList.size()+")"); } else { Real2 xy2 = atom.getXY2(); for (int i = 0; i < ligandHydrogenList.size(); i++) { ligandHydrogenList.get(i).setXY2(xy2.plus(vectorList.get(i))); } } } private static Transform2 PI120 = new Transform2(new Angle(Math.PI * 2./3.)); private static Transform2 PI90 = new Transform2(new Angle(Math.PI * 0.5)); private static Transform2 PI270 = new Transform2(new Angle(Math.PI * 1.5)); private static final Transform2 ROT90 = new Transform2(new Angle(Math.PI/2.)); private static void addCoords(CMLAtom atom, List vector3List, List hydrogenLigandList) { Point3 atomxyz3 = atom.getXYZ3(); for (int i = 0; i < vector3List.size(); i++) { Point3 xyz3 = atomxyz3.plus(vector3List.get(i)); hydrogenLigandList.get(i).setXYZ3(xyz3); } } private static List addCoords(CMLAtom atom, List ligandList, List hydrogenList, double bondLength) { List vectorList = new ArrayList(); if (hydrogenList.size() == 0) { // nothing to do } else if (ligandList.size() == 0) { if (hydrogenList.size() == 1) { vectorList.add(new Vector2(0, bondLength)); } else if (hydrogenList.size() == 2) { vectorList.add(new Vector2(0, bondLength)); vectorList.add(new Vector2(0, -bondLength)); } else if (hydrogenList.size() == 3) { vectorList.add(new Vector2(0, bondLength)); vectorList.add(new Vector2(bondLength * Math.sqrt(0.75), -bondLength *0.5)); vectorList.add(new Vector2(-bondLength * Math.sqrt(0.75), -bondLength *0.5)); } else if (hydrogenList.size() == 4) { vectorList.add(new Vector2(0, bondLength)); vectorList.add(new Vector2(0, -bondLength)); vectorList.add(new Vector2(bondLength, 0)); vectorList.add(new Vector2(-bondLength, 0)); } } else if (ligandList.size() == 1) { Vector2 ligandVector = new Vector2(ligandList.get(0).getXY2().subtract(atom.getXY2())); ligandVector = new Vector2(ligandVector.getUnitVector().multiplyBy(-bondLength)); if (hydrogenList.size() == 1) { vectorList.add(new Vector2(ligandVector)); } else if (hydrogenList.size() == 2) { Vector2 vector = new Vector2(ligandVector.multiplyBy(-1.0)); vector.transformBy(PI120); vectorList.add(new Vector2(vector)); vector.transformBy(PI120); vectorList.add(new Vector2(vector)); } else if (hydrogenList.size() == 3) { Vector2 vector = new Vector2(ligandVector); vectorList.add(new Vector2(vector)); vector.transformBy(PI90); vectorList.add(new Vector2(vector)); vector = new Vector2(ligandVector); vector.transformBy(PI270); vectorList.add(new Vector2(vector)); } else { } } else if (ligandList.size() == 2) { Vector2 ligandVector0 = new Vector2(ligandList.get(0).getXY2().subtract(atom.getXY2())); ligandVector0 = new Vector2(ligandVector0.getUnitVector()); Vector2 ligandVector1 = new Vector2(ligandList.get(1).getXY2().subtract(atom.getXY2())); ligandVector1 = new Vector2(ligandVector1.getUnitVector()); Angle angle = ligandVector0.getAngleMadeWith(ligandVector1); angle.setRange(Angle.Range.SIGNED); Vector2 bisectVector = null; boolean nearlyLinear = Math.abs(angle.getRadian()) > 0.9 * Math.PI; if (nearlyLinear) { bisectVector = new Vector2(ligandVector0.getUnitVector()); bisectVector.transformBy(ROT90); bisectVector.multiplyBy(bondLength); } else { bisectVector = new Vector2(ligandVector0.plus(ligandVector1)); bisectVector = new Vector2(bisectVector.getUnitVector()); bisectVector = new Vector2(bisectVector.multiplyBy(-bondLength)); } if (hydrogenList.size() == 1) { Vector2 vector = new Vector2(bisectVector); vector = new Vector2(vector.multiplyBy(1.0)); vectorList.add(vector); } else if (hydrogenList.size() == 2) { if (nearlyLinear) { vectorList.add(new Vector2(bisectVector)); vectorList.add(new Vector2(bisectVector.multiplyBy(-1.))); } else { Angle halfAngle = new Angle(Math.PI*0.5 - Math.abs(angle.getRadian()*0.5)); Transform2 t2 = new Transform2(halfAngle); Vector2 vector = new Vector2(bisectVector); vector.transformBy(t2); vectorList.add(vector); t2 = new Transform2(halfAngle.multiplyBy(-1.0)); vector = new Vector2(bisectVector); vector.transformBy(t2); vectorList.add(vector); } } else { } } else if (ligandList.size() == 3) { Vector2[] vectors = new Vector2[3]; Vector2 bisectVector = null; for (int i = 0; i < 3; i++) { vectors[i] = new Vector2(ligandList.get(i).getXY2().subtract(atom.getXY2())); bisectVector = (bisectVector == null) ? vectors[i] : new Vector2(bisectVector.plus(vectors[i])); } bisectVector = new Vector2(bisectVector.multiplyBy(-1.0)); // short vector try { bisectVector = new Vector2(bisectVector.getUnitVector().multiplyBy(vectors[0].getLength()*0.7)); // must not overlap too badly for (int i = 0; i < 3; i++) { Angle angle = bisectVector.getAngleMadeWith(vectors[i]); angle.setRange(Range.SIGNED); double angleR = Math.abs(angle.getRadian());; if (angleR < 0.2) { bisectVector = new Vector2(vectors[(i+1) % 3]); bisectVector = new Vector2(bisectVector.multiplyBy(-1.0)); break; } } } catch (EuclidRuntimeException e) { bisectVector = vectors[0]; bisectVector = new Vector2(bisectVector); } if (hydrogenList.size() == 1) { vectorList.add(new Vector2(bisectVector)); } else { } } else { // skip } return vectorList; } /** assume atom has correct count of hydrogens * overwrite all existing coordinates */ public static void addCalculated3DCoordinatesForExistingHydrogens(CMLAtom atom) { Double length = 1.6; // default for unusual atoms if (ChemicalElement.AS.C.equals(atom.getElementType())) { length = 1.08; } else if (ChemicalElement.AS.N.equals(atom.getElementType())) { length = 1.03; } else if (ChemicalElement.AS.O.equals(atom.getElementType())) { length = 0.96; } if (length != null) { addCalculated3DCoordinatesForExistingHydrogens(atom, length); removeHydrogenCountAttribute(atom); } } public static void removeHydrogenCountAttribute(CMLAtom atom) { Attribute att = atom.getAttribute("hydrogenCount"); if (att != null) { att.detach(); } } private final static double TWOPI3 = Math.PI*2.0/3.0; private final static double COS2PI3 = Math.cos(TWOPI3); private final static double SIN2PI3 = Math.sin(TWOPI3); private final static double ROOT3 = Math.sqrt(3.0); private final static double TETANG = 2*Math.atan(Math.sqrt(2.0)); private final static double TETANG0 = Math.PI - TETANG; private final static double TWOPI30 = Math.PI-TWOPI3; /** assume atom has correct count of hydrogens * overwrite all existing coordinates */ public static void addCalculated3DCoordinatesForExistingHydrogens(CMLAtom atom, double length) { List nonHydrogenLigandList = getNonHydrogenLigandList(atom); List hydrogenLigandList = getHydrogenLigandList(atom); int nonhCount = nonHydrogenLigandList.size(); int hCount = hydrogenLigandList.size(); int coordNumber = nonhCount + hCount; List vector3List = new ArrayList(); if (nonhCount == 0) { vector3List = addCoords0(atom, nonHydrogenLigandList, hydrogenLigandList, length); } else if (nonhCount == 1) { addCoords1(atom, nonHydrogenLigandList, hydrogenLigandList, length); } else if (nonhCount == 2) { addCoords2(atom, nonHydrogenLigandList, hydrogenLigandList, length); } else if (nonhCount == 3) { addCoords3(atom, nonHydrogenLigandList, hydrogenLigandList, length); } else { // cannot add hydrogens } addCoords(atom, vector3List, hydrogenLigandList); } private static double DTORAD = Math.PI / 180.; private static List addCoords0(CMLAtom atom, List nonHydrogenLigandList, List hydrogenLigandList, double length) { List vector3List = new ArrayList(); Vector3 vector0 = new Vector3(0.0, 0.0, length); String elementType = atom.getElementType(); if (hydrogenLigandList.size() == 0) { // nothing to add } else if (hydrogenLigandList.size() == 1) { vector3List.add(vector0); } else if (hydrogenLigandList.size() == 2) { vector3List.add(vector0); double angle = Math.PI; if (ChemicalElement.AS.O.equals(elementType)) { angle = 104 * DTORAD; } vector3List.add(new Vector3(0.0, length * Math.sin(angle), length * Math.cos(angle))); } else if (hydrogenLigandList.size() == 3) { vector3List.add(vector0); vector3List.add(new Vector3(0.0, length*COS2PI3, -length*SIN2PI3)); vector3List.add(new Vector3(0.0, length*COS2PI3, length*SIN2PI3)); } else if (hydrogenLigandList.size() == 4) { vector3List.add(new Vector3(length/ROOT3, length/ROOT3, length/ROOT3)); vector3List.add(new Vector3(-length/ROOT3, length/ROOT3, -length/ROOT3)); vector3List.add(new Vector3(length/ROOT3, -length/ROOT3, -length/ROOT3)); vector3List.add(new Vector3(-length/ROOT3, -length/ROOT3, length/ROOT3)); } return vector3List; } private static List addCoords1(CMLAtom atom, List nonHydrogenLigandList, List hydrogenLigandList, double length) { List vector3List = new ArrayList(); String elementType = atom.getElementType(); CMLAtomSet atomSet = null; if (hydrogenLigandList.size() == 0) { // nothing to add } else if (hydrogenLigandList.size() == 1) { AtomGeometry atomGeometry = AtomGeometry.LINEAR; if (ChemicalElement.AS.O.equals(elementType)) { atomGeometry = AtomGeometry.TETRAHEDRAL; } if (ChemicalElement.AS.N.equals(elementType)) { atomGeometry = AtomGeometry.TETRAHEDRAL; } atomSet = calculate3DCoordinatesForLigands(atom, atomGeometry, length, TWOPI30); } else if (hydrogenLigandList.size() == 2) { atomSet = calculate3DCoordinatesForLigands(atom, AtomGeometry.TRIGONAL, length, TWOPI30); } else if (hydrogenLigandList.size() == 3) { atomSet = calculate3DCoordinatesForLigands(atom, AtomGeometry.TETRAHEDRAL, length, TETANG0); } if (atomSet != null) { vector3List = getVectorList(atom, atomSet); } return vector3List; } private static List addCoords2(CMLAtom atom, List nonHydrogenLigandList, List hydrogenLigandList, double length) { CMLAtomSet atomSet = null; List vector3List = new ArrayList(); if (hydrogenLigandList.size() == 0) { // nothing to add } else if (hydrogenLigandList.size() == 1) { atomSet = calculate3DCoordinatesForLigands(atom, AtomGeometry.TRIGONAL, length, TWOPI3); } else if (hydrogenLigandList.size() == 2) { atomSet = calculate3DCoordinatesForLigands(atom, AtomGeometry.TETRAHEDRAL, length, 2*TETANG); } if (atomSet != null) { vector3List = getVectorList(atom, atomSet); } return vector3List; } private static List addCoords3(CMLAtom atom, List nonHydrogenLigandList, List hydrogenLigandList, double length) { List vector3List = new ArrayList(); CMLAtomSet atomSet = null; if (hydrogenLigandList.size() == 0) { // nothing to add } else if (hydrogenLigandList.size() == 1) { atomSet = calculate3DCoordinatesForLigands(atom, AtomGeometry.TETRAHEDRAL, length, TETANG0); } if (atomSet != null) { vector3List = getVectorList(atom, atomSet); } return vector3List; } private static List getVectorList(CMLAtom atom, CMLAtomSet atomSet) { List vectorList = new ArrayList(); for (CMLAtom atom1 : atomSet.getAtoms()) { Point3 xyz31 = atom1.getXYZ3(); Vector3 vector3 = xyz31.subtract(atom.getXYZ3()); vectorList.add(vector3); } return vectorList; } /** * Gets the nonHydrogenLigandList attribute of the AtomImpl object * * @return The nonHydrogenLigandList value */ public static List getNonHydrogenLigandList(CMLAtom atom) { List newLigandList = new ArrayList(); List ligandList = atom.getLigandAtoms(); for (CMLAtom ligand : ligandList) { if (!AS.H.equals(ligand.getElementType())) { newLigandList.add(ligand); } } return newLigandList; } /** * * @return hydrogen ligands */ public static List getHydrogenLigandList(CMLAtom atom) { List ligands = atom.getLigandAtoms(); List hatoms = new ArrayList(); for (CMLAtom ligand : ligands) { if (AS.H.equals(ligand.getElementType())) { hatoms.add(ligand); } } return hatoms; } /** * Adds 3D coordinates for singly-bonded ligands of this. * *

	 *       this is labelled A.
	 *       Initially designed for hydrogens. The ligands of A are identified
	 *       and those with 3D coordinates used to generate the new points. (This
	 *       allows structures with partially known 3D coordinates to be used, as when
	 *       groups are added.)
	 *       "Bent" and "non-planar" groups can be formed by taking a subset of the
	 *       calculated points. Thus R-NH2 could use 2 of the 3 points calculated
	 *       from (1,iii)
	 *       nomenclature: "this" is point to which new ones are "attached".
	 *           this may have ligands B, C...
	 *           B may have ligands J, K..
	 *           points X1, X2... are returned
	 *       The cases (see individual routines, which use idealised geometry by default):
	 *       (0) zero ligands of A. The resultant points are randomly oriented:
	 *          (i) 1 points  required; +x,0,0
	 *          (ii) 2 points: use +x,0,0 and -x,0,0
	 *          (iii) 3 points: equilateral triangle in xy plane
	 *          (iv) 4 points x,x,x, x,-x,-x, -x,x,-x, -x,-x,x
	 *       (1a) 1 ligand(B) of A which itself has a ligand (J)
	 *          (i) 1 points  required; vector along AB vector
	 *          (ii) 2 points: 2 vectors in ABJ plane, staggered and eclipsed wrt J
	 *          (iii) 3 points: 1 staggered wrt J, the others +- gauche wrt J
	 *       (1b) 1 ligand(B) of A which has no other ligands. A random J is
	 *       generated and (1a) applied
	 *       (2) 2 ligands(B, C) of this
	 *          (i) 1 points  required; vector in ABC plane bisecting AB, AC. If ABC is
	 *              linear, no points
	 *          (ii) 2 points: 2 vectors at angle ang, whose resultant is 2i
	 *       (3) 3 ligands(B, C, D) of this
	 *          (i) 1 points  required; if A, B, C, D coplanar, no points.
	 *             else vector is resultant of BA, CA, DA
	 *
	 *       The method identifies the ligands without coordinates, calculates them
	 *       and adds them. It assumes that the total number of ligands determines the
	 *       geometry. This can be overridden by the geometry parameter. Thus if there
	 *       are three ligands and TETRAHEDRAL is given a pyramidal geometry is created
	 *
	 *       Inappropriate cases throw exceptions.
	 *
	 *       fails if atom itself has no coordinates or >4 ligands
	 *       see org.xmlcml.molutils.Molutils for more details
	 *
	 * 
* * @param atom * @param geometry * from: Molutils.DEFAULT, Molutils.ANY, Molutils.LINEAR, * Molutils.TRIGONAL, Molutils.TETRAHEDRAL * @param length * A-X length * @param angle * B-A-X angle (used for some cases) * @return atomSet with atoms which were calculated. If request could not be * fulfilled (e.g. too many atoms, or strange geometry) returns * empty atomSet (not null) * @throws RuntimeException */ public static CMLAtomSet calculate3DCoordinatesForLigands(CMLAtom atom, AtomGeometry geometry, double length, double angle) throws RuntimeException { Point3 thisPoint; // create sets of atoms with and without ligands CMLAtomSet noCoordsLigandsAS = new CMLAtomSet(); if (atom.getX3Attribute() == null) { return noCoordsLigandsAS; } else { thisPoint = atom.getXYZ3(); } CMLAtomSet coordsLigandsAS = new CMLAtomSet(); // atomSet containing atoms without coordinates List ligandList = atom.getLigandAtoms(); for (CMLAtom ligandAtom : ligandList) { if (ligandAtom.getX3Attribute() == null) { noCoordsLigandsAS.addAtom(ligandAtom); } else { coordsLigandsAS.addAtom(ligandAtom); } } int nWithoutCoords = noCoordsLigandsAS.size(); int nWithCoords = coordsLigandsAS.size(); if (geometry.equals(AtomGeometry.DEFAULT)) { geometry = AtomGeometry.getGeometry(atom.getLigandAtoms().size()); } // too many ligands at present if (nWithCoords > 3) { CMLAtomSet emptyAS = new CMLAtomSet(); // FIXME?? return emptyAS; // nothing needs doing } else if (nWithoutCoords == 0) { return noCoordsLigandsAS; } List newPoints = null; List coordAtoms = coordsLigandsAS.getAtoms(); List noCoordAtoms = noCoordsLigandsAS.getAtoms(); if (nWithCoords == 0) { newPoints = Molutils.calculate3DCoordinates0(thisPoint, geometry.getIntValue(), length); } else if (nWithCoords == 1) { // ligand on A CMLAtom bAtom = (CMLAtom) coordAtoms.get(0); // does B have a ligand (other than A) CMLAtom jAtom = null; List bLigandList = bAtom.getLigandAtoms(); for (CMLAtom bLigand : bLigandList) { // FIXME: had to comment this out. Impossible to do. This is going to give bugs :( // if (!bLigand.equals(moleculeTool)) { // jAtom = bLigand; // break; //} } newPoints = Molutils.calculate3DCoordinates1(thisPoint, bAtom .getXYZ3(), (jAtom != null) ? jAtom.getXYZ3() : null, geometry.getIntValue(), length, angle); } else if (nWithCoords == 2) { Point3 bPoint = ((CMLAtom) coordAtoms.get(0)).getXYZ3(); Point3 cPoint = ((CMLAtom) coordAtoms.get(1)).getXYZ3(); newPoints = Molutils.calculate3DCoordinates2(thisPoint, bPoint, cPoint, geometry.getIntValue(), length, angle); } else if (nWithCoords == 3) { Point3 bPoint = ((CMLAtom) coordAtoms.get(0)).getXYZ3(); Point3 cPoint = ((CMLAtom) coordAtoms.get(1)).getXYZ3(); Point3 dPoint = ((CMLAtom) coordAtoms.get(2)).getXYZ3(); newPoints = new ArrayList(1); newPoints.add(Molutils.calculate3DCoordinates3(thisPoint, bPoint, cPoint, dPoint, length)); } int np = Math.min(noCoordsLigandsAS.size(), newPoints.size()); for (int i = 0; i < np; i++) { ((CMLAtom) noCoordAtoms.get(i)).setXYZ3(newPoints.get(i)); } return noCoordsLigandsAS; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy