org.xmlcml.cml.tools.MoleculeTool 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.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nu.xom.Attribute;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.Node;
import nu.xom.Nodes;
import nu.xom.ParentNode;
import nu.xom.Text;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.xmlcml.cml.attribute.UnitsAttribute;
import org.xmlcml.cml.base.CMLConstants;
import org.xmlcml.cml.base.CMLElement;
import org.xmlcml.cml.base.CMLElements;
import org.xmlcml.cml.base.CMLUtil;
import org.xmlcml.cml.base.CMLElement.CoordinateType;
import org.xmlcml.cml.base.CMLElement.FormalChargeControl;
import org.xmlcml.cml.base.CMLElement.Hybridization;
import org.xmlcml.cml.base.CMLLog.Severity;
import org.xmlcml.cml.element.CMLAmount;
import org.xmlcml.cml.element.CMLAngle;
import org.xmlcml.cml.element.CMLAtom;
import org.xmlcml.cml.element.CMLAtomArray;
import org.xmlcml.cml.element.CMLAtomSet;
import org.xmlcml.cml.element.CMLBond;
import org.xmlcml.cml.element.CMLBondArray;
import org.xmlcml.cml.element.CMLBondSet;
import org.xmlcml.cml.element.CMLBondStereo;
import org.xmlcml.cml.element.CMLCrystal;
import org.xmlcml.cml.element.CMLElectron;
import org.xmlcml.cml.element.CMLFormula;
import org.xmlcml.cml.element.CMLLabel;
import org.xmlcml.cml.element.CMLLength;
import org.xmlcml.cml.element.CMLLink;
import org.xmlcml.cml.element.CMLMap;
import org.xmlcml.cml.element.CMLMatrix;
import org.xmlcml.cml.element.CMLMolecule;
import org.xmlcml.cml.element.CMLMoleculeList;
import org.xmlcml.cml.element.CMLName;
import org.xmlcml.cml.element.CMLProperty;
import org.xmlcml.cml.element.CMLSymmetry;
import org.xmlcml.cml.element.CMLTorsion;
import org.xmlcml.cml.element.CMLTransform3;
import org.xmlcml.cml.element.CMLMap.Direction;
import org.xmlcml.cml.element.CMLMolecule.HydrogenControl;
import org.xmlcml.cml.graphics.CMLDrawable;
import org.xmlcml.cml.graphics.GraphicsElement;
import org.xmlcml.cml.graphics.SVGElement;
import org.xmlcml.cml.graphics.SVGG;
import org.xmlcml.cml.graphics.SVGGBox;
import org.xmlcml.cml.graphics.SVGRect;
import org.xmlcml.cml.graphics.SVGText;
import org.xmlcml.cml.tools.MoleculeDisplay.Position;
import org.xmlcml.euclid.Angle;
import org.xmlcml.euclid.EuclidRuntimeException;
import org.xmlcml.euclid.Point3;
import org.xmlcml.euclid.Point3Vector;
import org.xmlcml.euclid.Real;
import org.xmlcml.euclid.Real2;
import org.xmlcml.euclid.Real2Interval;
import org.xmlcml.euclid.Real2Range;
import org.xmlcml.euclid.Real3Range;
import org.xmlcml.euclid.RealRange;
import org.xmlcml.euclid.RealSquareMatrix;
import org.xmlcml.euclid.Transform2;
import org.xmlcml.euclid.Transform3;
import org.xmlcml.euclid.Util;
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 org.xmlcml.molutil.ChemicalElement.Type;
/**
* additional tools for molecule. not fully developed
*
* @author pmr
*
*/
public class MoleculeTool extends AbstractSVGTool {
static final Logger LOG = Logger.getLogger(MoleculeTool.class.getName());
/** dewisott */
public static String HYDROGEN_COUNT = "hydrogenCount";
private CMLMolecule molecule;
private Map atomToolMap;
private Map bondToolMap;
private SelectionTool selectionTool;
private MoleculeDisplay moleculeDisplay;
private CMLAtom currentAtom;
private CMLBond currentBond;
private Morgan morgan;
private SVGRect boundingRectSVG;
/**
* constructor
*
* @param molecule
* @deprecated use getOrCreateTool
*/
public MoleculeTool(CMLMolecule molecule) {
if (molecule == null) {
throw new RuntimeException("null molecule");
}
this.molecule = molecule;
this.molecule.setTool(this);
}
/**
* get molecule.
*
* @return the molecule
*/
public CMLMolecule getMolecule() {
return molecule;
}
private void enableAtomToolMap() {
if (this.atomToolMap == null) {
this.atomToolMap = new HashMap();
}
}
private void enableBondToolMap() {
if (this.bondToolMap == null) {
this.bondToolMap = new HashMap();
}
}
/**
* @param atom
* @return atomTool (created if not present)
*/
public AtomTool getOrCreateAtomTool(CMLAtom atom) {
enableAtomToolMap();
AtomTool atomTool = atomToolMap.get(atom);
if (atomTool== null) {
atomTool = AtomTool.getOrCreateTool(atom);
atomToolMap.put(atom, atomTool);
}
return atomTool;
}
/**
* @param bond
* @return bondTool (created if not present)
*/
public BondTool getOrCreateBondTool(CMLBond bond) {
enableBondToolMap();
BondTool bondTool = bondToolMap.get(bond);
if (bondTool== null) {
bondTool = BondTool.getOrCreateTool(bond);
bondToolMap.put(bond, bondTool);
}
return bondTool;
}
/** gets MoleculeTool associated with molecule.
* if null creates one and sets it in molecule
* @param molecule
* @return tool
*/
@SuppressWarnings("all")
public static MoleculeTool getOrCreateTool(CMLMolecule molecule) {
MoleculeTool moleculeTool = null;
if (molecule != null) {
moleculeTool = (MoleculeTool) molecule.getTool();
if (moleculeTool == null) {
moleculeTool = new MoleculeTool(molecule);
molecule.setTool(moleculeTool);
}
}
return moleculeTool;
}
/** get charge.
*
* @return charge
*/
public int getFormalCharge() {
int formalCharge = 0;
Nodes chargedAtoms = molecule.getAtomArray().query(".//"+CMLAtom.NS+"[@formalCharge]", CMLConstants.CML_XPATH);
for (int i = 0; i < chargedAtoms.size(); i++) {
formalCharge += Integer.parseInt(((Element)chargedAtoms.get(i)).getAttributeValue("formalCharge"));
}
return formalCharge;
}
/** get charged atoms.
*
* @return atoms
*/
public List getChargedAtoms() {
List chargedAtoms = new ArrayList();
Nodes atoms = molecule.getAtomArray().query(".//"+CMLAtom.NS+"[@formalCharge != 0]", CMLConstants.CML_XPATH);
for (int i = 0; i < atoms.size(); i++) {
chargedAtoms.add((CMLAtom)atoms.get(i));
}
return chargedAtoms;
}
/**
* Adjust bond orders to satisfy valence.
*
* in impossible systems appropriate atoms are marked as radicals
* (spinMultiplicity) assumes explicit hydrogens
* uses default PISystemManager
*/
public void adjustBondOrdersToValency() {
molecule.setBondOrders(CMLBond.SINGLE_S);
PiSystemControls piSystemManager = new PiSystemControls();
piSystemManager.setUpdateBonds(true);
// piSystemManager.setKnownUnpaired(knownUnpaired);
piSystemManager.setDistributeCharge(true);
this.adjustBondOrdersToValency(piSystemManager);
}
/**
* Adjust bond orders to satisfy valence.
*
* in impossible systems appropriate atoms are marked as radicals
* (spinMultiplicity) assumes explicit hydrogens
*
* @param piSystemManager
*/
public void adjustBondOrdersToValency(PiSystemControls piSystemManager) {
// normalize bond orders
molecule.setNormalizedBondOrders();
PiSystem piSystem2 = new PiSystem(molecule.getAtoms());
piSystem2.setPiSystemManager(new PiSystemControls(piSystemManager));
List piSystemList2 = piSystem2.generatePiSystemList();
for (PiSystem subPiSystem : piSystemList2) {
List atomList = subPiSystem.getAtomList();
int npi = atomList.size();
if (npi < 2) {
// LOG.debug("Cannot find pi system for " + npi);
} else {
subPiSystem.identifyDoubleBonds();
}
}
// remove temporary pi electrons
List electrons = CMLUtil.getQueryNodes(molecule, ".//"+CMLElectron.NS+"[@dictRef='cml:piElectron']", CMLConstants.CML_XPATH);
for (Node electron : electrons) {
electron.detach();
}
normalizeBondOrders();
}
public void normalizeBondOrders() {
List bonds = molecule.getBonds();
for (CMLBond bond : bonds) {
// bond.debug("XXXX");
BondOrder.normalizeBondOrder(bond);
// bond.debug("BBBBBBBBBBB");
}
}
/**
<<<<<<< .working
=======
* get the double bond equivalents.
*
* this is the number of double bonds the atom can make an sp2 atom has 1 an
* sp atom has 2
*
* @param atom
* @param fcd
* @return the bond sum (0, 1, 2)
* @throws RuntimeException
* if cannot get formal charges
*/
public int getDoubleBondEquivalents(CMLAtom atom, FormalChargeControl fcd) {
if (atom.getMolecule() == null) {
throw new RuntimeException("WARNING skipping DBE");
}
int valenceElectrons = atom.getValenceElectrons();
int formalCharge = 0;
try {
formalCharge = atom.getFormalCharge(fcd);
} catch (RuntimeException e) {
e.printStackTrace();
}
valenceElectrons -= formalCharge;
int maxBonds = (valenceElectrons < 4) ? valenceElectrons
: 8 - valenceElectrons;
// hydrogens are include in bondSum
int doubleBondEquivalents = maxBonds - this.getBondOrderSum(atom);
return doubleBondEquivalents;
}
/**
* get sum of formal bondOrders. uses the actual ligands (i.e. implicit
* hydrogens are not used) aromatic bonds are counted as 1.5, 1,2,3 aromatic
* bonds add 1 double bond
*
* @param atom
* @return sum (-1 means cannot be certain)
*/
public int getBondOrderSum(CMLAtom atom) {
int multipleBondSum = 0;
int aromaticBondSum = 0;
boolean ok = true;
List ligandBonds = atom.getLigandBonds();
for (CMLBond ligandBond : ligandBonds) {
// if the bond is to a H atom then don't increment
// multipleBondSum as H's are dealt with further down
// the method.
boolean hasH = false;
for (CMLAtom bondAt : ligandBond.getAtoms()) {
if (AS.H.equals(bondAt.getElementType()) && bondAt != atom) {
hasH = true;
}
}
if (hasH) {
continue;
}
String order = ligandBond.getOrder();
if (order == null) {
multipleBondSum += 0;
} else if (CMLBond.isDouble(order)) {
multipleBondSum += 2;
} else if (CMLBond.isTriple(order)) {
multipleBondSum += 3;
} else if (CMLBond.isSingle(order)) {
multipleBondSum += 1;
} else if (order.equals(CMLBond.AROMATIC)) {
aromaticBondSum += 1;
} else {
LOG.info("Unknown bond order:" + order + CMLConstants.S_COLON);
ok = false;
}
}
// any aromatic bonds dictate SP2, so...
if (aromaticBondSum > 0) {
multipleBondSum = atom.getLigandBonds().size() + 1;
}
int hydrogenCount = 0;
try {
hydrogenCount = atom.getHydrogenCount();
} catch (RuntimeException e) {
}
multipleBondSum += hydrogenCount;
return ((ok) ? multipleBondSum : -1);
}
/**
* calculates the geometrical hybridization.
*
* calculates the geometrical hybridisation from 3D coords tetrahedron or
* pyramid gives SP3 trigonal gives SP2 bent gives BENT linear gives SP
* square planar gives SQ
*
* pyramidal and planar are distinguished by improper dihedral of 18 degrees
*
* @param atom
* @return hybridisation (SP3, SP2, BENT, SP, SQUARE_PLANAR)
*/
public Hybridization getGeometricHybridization(CMLAtom atom) {
List ligands = atom.getLigandAtoms();
Hybridization geomHybridization = null;
if (ligands.size() <= 1 || ligands.size() > 4) {
return null;
}
Point3 thisXyz = atom.getXYZ3();
Angle angle = null;
if (thisXyz == null) {
} else if (ligands.size() == 2) {
Point3 thisXyz1 = ligands.get(0).getXYZ3();
Point3 thisXyz2 = ligands.get(1).getXYZ3();
try {
angle = Point3.getAngle(thisXyz1, thisXyz, thisXyz2);
if (angle.getDegrees() > 150) {
geomHybridization = Hybridization.SP;
} else {
geomHybridization = Hybridization.BENT;
// if (angle.getDegrees() > 115) return CMLAtom.SP2;
// return CMLAtom.SP3;
}
} catch (Exception e) {
LOG.error("BUG " + e);
}
} else if (ligands.size() == 3) {
Point3 thisXyz1 = ligands.get(0).getXYZ3();
Point3 thisXyz2 = ligands.get(1).getXYZ3();
Point3 thisXyz3 = ligands.get(2).getXYZ3();
// improper dihedral
try {
angle = Point3
.getTorsion(thisXyz1, thisXyz2, thisXyz3, thisXyz);
geomHybridization = Hybridization.SP2;
if (Math.abs(angle.getDegrees()) > 18) {
geomHybridization = Hybridization.SP3;
}
} catch (Exception e) {
LOG.error("BUG " + e);
}
} else {
geomHybridization = Hybridization.SP3;
}
return geomHybridization;
}
/**
* 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
*
* @param atom
* @return group
*/
public int getHydrogenValencyGroup(CMLAtom atom) {
final int[] group = { 1, 4, 5, 6, 7, 4, 5, 6, 7, 7, 7 };
final int[] eneg0 = { 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1 };
final int[] eneg1 = { 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1 };
int elNum = -1;
try {
String elType = atom.getElementType();
elNum = CMLAtom.getCommonElementSerialNumber(elType);
if (elNum == -1) {
return -1;
}
if (eneg0[elNum] == 0) {
return group[elNum];
}
// if atom is susceptible to enegative ligands, exit if they are
// present
List ligandList = atom.getLigandAtoms();
for (CMLAtom lig : ligandList) {
int ligElNum = CMLAtom.getCommonElementSerialNumber(lig
.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;
}
/**
* Add or delete hydrogen atoms to satisy 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 void adjustHydrogenCountsToValency(CMLAtom atom,
CMLMolecule.HydrogenControl control) {
if (atom.getHydrogenCountAttribute() == null) {
int group = this.getHydrogenValencyGroup(atom);
if (group == -1) {
return;
} else if (group == -2) {
return;
}
// hydrogen and metals
if (group < 4) {
return;
}
int sumBo = getSumNonHydrogenBondOrder(molecule, 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;
}
atom.setHydrogenCount(nh);
this.expandImplicitHydrogens(atom, control);
}
}
/**
>>>>>>> .merge-right.r915
* get calculated molecular mass.
* uses hydrogenCount attribute to check hydrogens
* @return calculated molecular mass.
* @throws RuntimeException unknown/unsupported element type (Dummy counts as zero mass)
*/
public double getCalculatedMolecularMass() throws RuntimeException {
getTotalHydrogenCount();
return this.getCalculatedMolecularMass(HydrogenControl.USE_HYDROGEN_COUNT);
}
/** gets total hydrogen count on molecule.
* if molecule@hydrogenCount uses that
* else ensures all atoms have hydrogenCount
* if not uses adjustHydrogenCountsToValency()
* then summs over all atoms
* then adds hydrogenCountAttribute
*
* THIS IS A MENACE - it calculates new hydrogens
* @return total hydrogen count
*/
@Deprecated
public int getTotalHydrogenCount() {
String hydrogenCount = molecule.getAttributeValue(HYDROGEN_COUNT);
int sum = -1;
if (hydrogenCount == null || CMLConstants.S_EMPTY.equals(hydrogenCount.trim())) {
this.adjustHydrogenCountsToValency(HydrogenControl.NO_EXPLICIT_HYDROGENS);
sum = sumHydrogenCountOnAtoms();
molecule.addAttribute(new Attribute(HYDROGEN_COUNT, ""+sum));
} else {
sum = Integer.parseInt(hydrogenCount);
}
return sum;
}
private int sumHydrogenCountOnAtoms() {
int sum = 0;
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
if (!AS.H.equals(atom.getElementType())) {
sum += atom.getHydrogenCount();
}
}
return sum;
}
/**
<<<<<<< .working
=======
* 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
*
* @param atom
* @exception RuntimeException
* null atom in argument
* @return sum of bond orders. May be 0 for isolated atom or atom with only
* H ligands
*/
public int getSumNonHydrogenBondOrder(CMLAtom atom) {
return MoleculeTool.getSumNonHydrogenBondOrder(this.molecule, atom);
}
/**
* 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
* @param molecule
*
* @param atom
* @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(CMLMolecule molecule, CMLAtom atom)
throws RuntimeException {
float sumBo = 0.0f;
List ligandList = atom.getLigandAtoms();
for (CMLAtom ligand : ligandList) {
if (AS.H.equals(ligand.getElementType())) {
continue;
}
CMLBond bond = molecule.getBond(atom, ligand);
if (bond == null) {
throw new RuntimeException(
"Serious bug in getSumNonHydrogenBondOrder");
}
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);
}
/**
>>>>>>> .merge-right.r915
* deletes a hydrogen from an atom.
*
* used for building up molecules. If there are implicit H atoms it reduces
* the hydrogenCount by 1. If H's are explicit it removes the first hydrogen
* ligand
*
* @param atom
<<<<<<< .working
* @deprecated use AtomTool method
* @exception RuntimeException
=======
* @exception RuntimeException
>>>>>>> .merge-right.r915
* no hydrogen ligands on atom
*
*/
public void deleteHydrogen(CMLAtom atom) {
if (atom.getHydrogenCountAttribute() != null) {
if (atom.getHydrogenCount() > 0) {
atom.setHydrogenCount(atom.getHydrogenCount() - 1);
} else {
throw new RuntimeException("No hydrogens to delete");
}
} else {
Set hSet = ChemicalElement
.getElementSet(new String[] { AS.H.value });
List hLigandVector = CMLAtom.filter(atom.getLigandAtoms(),
hSet);
if (hLigandVector.size() > 0) {
molecule.deleteAtom(hLigandVector.get(0));
} else {
throw new RuntimeException("No hydrogens to delete");
}
}
Set hSet = ChemicalElement.getElementSet(
new String[] { AS.H.value });
List hLigandVector = CMLAtom.filter(atom.getLigandAtoms(),
hSet);
if (hLigandVector.size() > 0) {
molecule.deleteAtom(hLigandVector.get(0));
}
}
/**
* gets all atoms downstream of a bond.
*
* recursively visits all atoms in branch until all leaves have been
* visited. if branch is cyclic, halts when it rejoins atom or otherAtom
* Example:
*
*
*
* MoleculeTool moleculeTool ... contains relevant molecule
* CMLBond b ... (contains atom)
* CMLAtom otherAtom = b.getOtherAtom(atom);
* AtomSet ast = moleculeTool.getDownstreamAtoms(atom, otherAtom);
*
* @param atom
* @param otherAtom not to be visited
* @return the atomSet (empty if none)
*
*/
public CMLAtomSet getDownstreamAtoms(CMLAtom atom, CMLAtom otherAtom) {
CMLAtomSet atomSet = new CMLAtomSet();
boolean forceUpdate = false;
getDownstreamAtoms(atom, atomSet, otherAtom, forceUpdate);
atomSet.updateContent();
return atomSet;
}
/**
* gets all atoms downstream of a bond.
* VERY SLOW I THINK
* recursively visits all atoms in branch until all leaves have been
* visited. if branch is cyclic, halts when it rejoins atom or otherAtom the
* routine is passed a new AtomSetImpl to be populated
*
* Example: CMLBond b ... (contains atom)
* CMLAtom otherAtom = b.getOtherAtom(atom);
* AtomSet ast = new AtomSetImpl();
* atom.getDownstreamAtoms(ast, otherAtom);
*
* @param atom
* @param atomSet
* to accumulate ligand atoms
* @param otherAtom
* not to be visited
*
*/
private void getDownstreamAtoms(CMLAtom atom, CMLAtomSet atomSet,
CMLAtom otherAtom, boolean forceUpdate) {
atomSet.addAtom(atom, forceUpdate);
List ligandList = atom.getLigandAtoms();
for (CMLAtom ligandAtom : ligandList) {
// do not revisit atoms
if (atomSet.contains(ligandAtom)) {
;
// do not backtrack
} else if (ligandAtom.equals(otherAtom)) {
;
} else {
this.getDownstreamAtoms(ligandAtom, atomSet, atom, forceUpdate);
}
}
}
/**
* append to id creates new id, perhaps to disambiguate
*
* @param atom
* @param s
*/
public void appendToId(CMLAtom atom, final String s) {
String id = atom.getId();
if ((id != null) && (id.length() > 0)) {
atom.renameId(id + s);
} else {
atom.renameId(s);
}
}
/**
* 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 CMLAtomSet calculate3DCoordinatesForLigands(CMLAtom atom,
int geometry, double length, double angle) {
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 == Molutils.DEFAULT) {
geometry = 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, 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) {
if (!bLigand.equals(this)) {
jAtom = bLigand;
break;
}
}
newPoints = Molutils.calculate3DCoordinates1(thisPoint, bAtom
.getXYZ3(), (jAtom != null) ? jAtom.getXYZ3() : null,
geometry, 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, 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;
}
// ============ atom matcher ==============
/**
* finds a pair with neighbours that map to each other can be called
* iteratively until returns null
*
* @param atomSet1
* to match
* @param atomSet2
* diminished if match found
* @param from1to2map
* the mapping from atomSet1 to atomSet2 (i.e. from ids in
* atomSet1, to in atomSet2)
* @param atomMatcher
* @return pair of matched atoms
*
*/
// FIXME
@SuppressWarnings("unused")
private AtomPair getAtomsWithSameMappedNeighbours00(
AtomMatcher atomMatcher, CMLAtomSet atomSet1, CMLAtomSet atomSet2,
CMLMap from1to2map) {
Set matchingSet = new HashSet();
AtomPair pair = null;
for (CMLAtom atom : atomSet1.getAtoms()) {
if (atomMatcher.skipAtom(atom)) {
continue;
}
matchingSet.add(atom);
}
for (CMLAtom atom : atomSet2.getAtoms()) {
String elementType2 = atom.getElementType();
if (atomMatcher.skipAtom(atom)) {
continue;
}
CMLAtomSet ligand2Set = CMLAtomSet.createFromAtoms(atom.getLigandAtoms());
// for each ligandList or atom2 loop through all atom1s to find
// match
// which has most ligands?
// FIXME better - store all atoms with at least one ligand
List matchAtomList = new ArrayList();
int maxLigands = 0;
CMLAtom mostLigandedAtom1 = null;
for (int j = 0; j < atomSet1.size(); j++) {
CMLAtom atom1 = (CMLAtom) atomSet1.getAtom(j);
if (atomMatcher.skipAtom(atom1)) {
continue;
}
String elementType1 = atom1.getElementType();
// atoms must match type
if (!elementType1.equals(elementType2)) {
continue;
}
int ligandCount = 0;
List ligandList = atom1.getLigandAtoms();
for (CMLAtom ligandAtom1 : ligandList) {
if (!atomMatcher.skipLigandAtom(ligandAtom1)) {
String id1 = ligandAtom1.getId();
String id2 = from1to2map.getRef(id1,
CMLMap.Direction.FROM);
// does id2 exist and is it a ligand of atom 2
if (id2 != null && ligand2Set.getAtomById(id2) != null) {
ligandCount++;
}
}
}
if (ligandCount > maxLigands) {
maxLigands = ligandCount;
mostLigandedAtom1 = atom1;
}
if (ligandCount > 0) {
matchAtomList.add(atom1);
}
}
// ligand atoms in common?
//
if (matchAtomList.size() == 1) {
CMLAtom matchAtom1 = matchAtomList.get(0);
pair = new AtomPair(matchAtom1, atom);
atomSet1.removeAtom(matchAtom1);
atomSet2.removeAtom(atom);
break;
// old method
} else if (false && mostLigandedAtom1 != null) {
pair = new AtomPair(mostLigandedAtom1, atom);
atomSet1.removeAtom(mostLigandedAtom1);
atomSet2.removeAtom(atom);
break;
} else {
pair = null;
}
}
return pair;
}
public List getAtomList(CMLMap map, Direction toFrom) {
List idList = map.getRefs(toFrom);
List atomList = new ArrayList();
for (String id : idList) {
CMLAtom atom = molecule.getAtomById(id);
if (atom == null) {
// maybe a fromSet
// for (String ss : idList) {
// LOG.debug(ss);
// }
// map.debug();
// throw new RuntimeException("missing atom: "+id);
}
atomList.add(atom);
}
return atomList;
}
/**
* Expand implicit hydrogen atoms.
*
* This needs looking at
*
* CMLMolecule.NO_EXPLICIT_HYDROGENS
* CMLMolecule.USE_HYDROGEN_COUNT // no
* action
*
* @param atom
* @param control
* @throws RuntimeException
*/
public void expandImplicitHydrogens(CMLAtom atom,
CMLMolecule.HydrogenControl control) throws RuntimeException {
if (control.equals(CMLMolecule.HydrogenControl.USE_HYDROGEN_COUNT)) {
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 needs rethinking
if (control.equals(CMLMolecule.HydrogenControl.NO_EXPLICIT_HYDROGENS)
&& currentHCount != 0) {
return;
}
String id = atom.getId();
for (int i = 0; i < hydrogenCount - currentHCount; i++) {
CMLAtom hatom = new CMLAtom(id + "_h" + (i + 1));
molecule.addAtom(hatom);
hatom.setElementType(AS.H.value);
CMLBond bond = new CMLBond(atom, hatom);
molecule.addBond(bond);
bond.setOrder("1");
}
}
/**
* gets lone electrons on atom. electrons not involved in bonding assumes
* accurate hydrogen count only currently really works for first row (C, N,
* O, F) calculated as getHydrogenValencyGroup() -
* (getSumNonHydrogenBondOrder() + getHydrogenCount()) -
* atom.getFormalCharge()
*
* @param atom
* @return number of lone electrons (< 0 means cannot calculate)
*/
public int getLoneElectronCount(CMLAtom atom) {
int loneElectronCount = -1;
int group = this.getHydrogenValencyGroup(atom);
if (group == -1) {
return -1;
}
int sumNonHBo = MoleculeTool.getSumNonHydrogenBondOrder(molecule, atom);
int nHyd = atom.getHydrogenCount();
int formalCharge = 0;
if (atom.getFormalChargeAttribute() != null) {
formalCharge = atom.getFormalCharge();
}
loneElectronCount = group - (sumNonHBo + nHyd) - formalCharge;
return loneElectronCount;
}
@SuppressWarnings("unused")
private void getHybridizationFromConnectivty() {
// FIXME
List atoms = molecule.getAtoms();
// process atoms first. known terminal atoms or atoms with maximum
// connectivity
// gives precise information;
int valence[] = new int[atoms.size()];
int i = -1;
for (CMLAtom atom : molecule.getAtoms()) {
i++;
int bondOrder = -1;
int ligandCount = atom.getLigandAtoms().size();
String elType = null;
try {
elType = atom.getElementType();
} catch (Exception e) {
LOG.error("BUG " + e);
}
valence[i] = -1;
if (AS.H.equals(elType) || AS.F.equals(elType) || AS.Cl.equals(elType)
|| AS.Br.equals(elType) || AS.I.equals(elType)) {
valence[i] = 1;
} else if (AS.O.equals(elType) || AS.S.equals(elType)) {
valence[i] = 2;
} else if (AS.N.equals(elType)) {
valence[i] = 3;
} else if (AS.C.equals(elType)) {
valence[i] = 4;
}
int valLig = valence[i] - ligandCount;
// FIXME many times!
if (valLig >= 0) {
if (valence[i] < 3) {
if (ligandCount == 1 || valLig == 0) {
// bondOrder = orderX[valLig];
}
// atom.setGeometricHybridization(spX[valLig]);
} else if (valence[i] == 4) {
if (ligandCount == 4) {
// bondOrder = orderX[valLig];
// atom.setGeometricHybridization(spX[valLig]);
}
}
}
if (bondOrder == -1) {
continue;
}
// set hybridization and orders for ligand bonds for (
// LigandIterator it = atom.getLigandIterator();
// it.hasNext();
// )
{
// CMLAtom ligand = (CMLAtom) it.next();
// FIXME
CMLAtom ligand = null;
CMLBond bond = molecule.getBond(atom, ligand);
// FIXME
// order = bond.getOrder();
}
if (bondOrder == -1) {
}
// bond.setOrder(S_EMPTY+bondOrder);
//
// bond.orderKnown = true;
}
boolean change = true;
while (change) {
change = false;
// iterate using the known bond orders and filling in any gaps...
for (int j = 0; j < atoms.size(); j++) {
if (valence[j] < 0) {
continue;
}
int bondSum = 0;
int aromSum = 0;
CMLBond missingBond[] = new CMLBond[atoms.size()];
int nMissing = 0;
for (CMLAtom atom1 : atoms) {
// CMLAtom ligand = (CMLAtom) it1.next();
CMLAtom ligand = null;
CMLBond bond = molecule.getBond(atom1, ligand);
String order = null;
try {
order = bond.getOrder();
} catch (Exception e) {
LOG.error("BUG " + e);
}
if (order.equals(CMLBond.UNKNOWN_ORDER)) {
missingBond[nMissing++] = bond;
} else {
if (CMLBond.isSingle(order)) {
bondSum++;
} else if (CMLBond.isDouble(order)) {
bondSum += 2;
} else if (CMLBond.isTriple(order)) {
bondSum += 3;
} else if (order.equals(CMLBond.AROMATIC)) {
aromSum++;
}
}
}
change = assignMissingBonds(valence, j, bondSum, aromSum,
missingBond, nMissing);
}
}
}
private boolean assignMissingBonds(int[] valence, int i, int bondSum,
int aromSum, CMLBond[] missingBond, int nMissing) {
boolean change = false;
if (nMissing > 0) {
if (aromSum == 0) {
int delta = valence[i] - bondSum;
if (nMissing == delta) {
for (int j = 0; j < nMissing; j++) {
try {
missingBond[j].setOrder(CMLBond.SINGLE_S);
} catch (Exception e) {
LOG.error("BUG " + e);
}
}
change = true;
} else if (nMissing == 1) {
String newOrder = null;
if (delta == 1) {
newOrder = CMLBond.SINGLE_S;
} else if (delta == 2) {
newOrder = CMLBond.DOUBLE_D;
} else if (delta == 3) {
newOrder = CMLBond.TRIPLE_T;
} else if (newOrder != null) {
try {
missingBond[0].setOrder(newOrder);
} catch (Exception e) {
LOG.error("BUG " + e);
}
change = true;
}
}
}
}
return change;
}
// =============== ATOMSET =========================
/**
* gets bondset for all bonds in current atom set. slow order of bond is
* undetermined but probably in atom document order and iteration through
* ligands
*
* @param atomSet
* @return the bondSet
*/
public CMLBondSet getBondSet(CMLAtomSet atomSet) {
// List atoms = molecule.getAtoms();
List atoms = atomSet.getAtoms();
molecule.getBonds();
CMLBondSet bondSet = new CMLBondSet();
for (CMLAtom atom : atoms) {
List ligandList = atom.getLigandAtoms();
List ligandBondList = atom.getLigandBonds();
// loop through ligands and examine each for membership of this set;
// if so add bond
int i = 0;
for (CMLAtom ligand : ligandList) {
CMLBond ligandBond = ligandBondList.get(i++);
if (bondSet.contains(ligandBond)) {
continue;
}
if (atomSet.contains(ligand)) {
bondSet.addBond(ligandBond);
}
}
}
return bondSet;
}
/**
* Creates new Molecule one ligand shell larger than the atomSet.
*
* @param atomSet
* to sprout from (must all be in this molecule).
* @return new Molecule (or null if no atoms)
*/
public CMLMolecule sprout(CMLAtomSet atomSet) {
CMLMolecule newMolecule = null;
if (atomSet != null && atomSet.getAtoms().size() > 0) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
CMLAtomSet newAtomSet = atomSetTool.sprout();
CMLMolecule molecule = atomSetTool.getMoleculeOrAncestor();
CMLBondSet newBondSet = MoleculeTool.getOrCreateTool(molecule)
.getBondSet(newAtomSet);
newMolecule = MoleculeTool.createMolecule(newAtomSet, newBondSet);
}
return newMolecule;
}
/**
* get bond connecting 2 atoms.
*
* @param a1
* first atom
* @param a2
* second atom
* @return bond or null if not found
*/
public CMLBond getBond(String id1, String id2) {
String atomHash = CMLBond.atomHash(id1, id2);
return getBondFromHash(atomHash);
}
private CMLBond getBondFromHash(String atomHash) {
CMLBond theBond = null;
if (atomHash != null) {
for (CMLBond bond : molecule.getBonds()) {
String bondHash = CMLBond.atomHash(bond);
if (bondHash.equals(atomHash)) {
theBond = bond;
break;
}
}
}
return theBond;
}
/**
* create a cluster of connected atoms of given types.
*
* @param typeList
* list of elements allowed
* @return list of clusters (or empty list)
*/
public List createClusters(List typeList) {
List clusterList = new ArrayList();
if (molecule.getMoleculeCount() > 0) {
for (CMLMolecule subMolecule : molecule.getMoleculeElements()) {
MoleculeTool subMoleculeTool = MoleculeTool.getOrCreateTool(subMolecule);
List subClusterList = subMoleculeTool
.createClusters(typeList);
for (CMLMolecule subCluster : subClusterList) {
clusterList.add(subCluster);
}
}
} else {
CMLAtomSet unusedAtomSet = this.getAtomSet();
while (unusedAtomSet.size() > 0) {
CMLAtomSet clusterSet = new CMLAtomSet();
expandCluster(clusterSet, unusedAtomSet,
typeList);
if (clusterSet.size() > 1) {
CMLMolecule clusterMolecule = MoleculeTool.createMolecule(clusterSet);
MoleculeTool.getOrCreateTool(clusterMolecule).calculateBondedAtoms();
clusterList.add(clusterMolecule);
molecule.addToLog(Severity.INFO, "NEW CLUSTER SIZE "
+ clusterSet.size());
}
}
}
return clusterList;
}
private boolean expandCluster(CMLAtomSet clusterSet,
CMLAtomSet unusedAtomSet, List typeList) {
boolean change = false;
List unusedAtomList = unusedAtomSet.getAtoms();
for (CMLAtom currentAtom : unusedAtomList) {
if (sproutAtom(currentAtom, clusterSet,
unusedAtomSet, typeList)) {
change = true;
break;
}
}
return change;
}
private boolean sproutAtom(CMLAtom currentAtom, CMLAtomSet clusterSet,
CMLAtomSet unusedAtomSet, List typeList) {
boolean change = false;
unusedAtomSet.removeAtom(currentAtom);
if (currentAtom.atomIsCompatible(typeList)) {
change = true;
if (!clusterSet.contains(currentAtom)) {
clusterSet.addAtom(currentAtom);
}
List ligandList = currentAtom.getLigandAtoms();
for (CMLAtom ligand : ligandList) {
// atom already used?
if (!unusedAtomSet.contains(ligand)) {
continue;
}
// atom not of right kind
if (!ligand.atomIsCompatible(typeList)) {
continue;
}
boolean bonded = CMLBond.areWithinBondingDistance(currentAtom,
ligand);
if (bonded) {
clusterSet.addAtom(ligand);
}
sproutAtom(ligand, clusterSet, unusedAtomSet, typeList);
}
}
return change;
}
/**
* extract ligands from connected molecule components.
*
* @param typeList
* list of elements allowed
* @return list of clusters (or empty list)
*/
public List createLigands(List typeList) {
new ConnectionTableTool(molecule).partitionIntoMolecules();
List ligandList = new ArrayList();
for (CMLMolecule splitMol : this.getMoleculeList()) {
CMLMolecule subMolecule = new CMLMolecule(splitMol);
//subMolecule.setId("NEW_" + subMolecule.getId());
boolean deleted = false;
List atomList = subMolecule.getAtoms();
for (CMLAtom atom : atomList) {
if (atom.atomIsCompatible(typeList)) {
deleted = true;
subMolecule.deleteAtom(atom);
}
}
if (deleted) {
if (subMolecule.getAtomCount() == 0) {
continue;
} else {
new ConnectionTableTool(subMolecule).partitionIntoMolecules();
List ligands = MoleculeTool.getOrCreateTool(subMolecule).getMoleculeList();
for (CMLMolecule ligand : ligands) {
ligandList.add(ligand);
}
}
}
}
return ligandList;
}
public void addGroupLabels() {
for (CMLAtom atom : molecule.getAtoms()) {
AtomTool atomTool = AtomTool.getOrCreateTool(atom);
atomTool.createGroupLabelAndAtomSet();
}
}
public void addFormula() {
if (molecule.getFormulaElements().size() == 0) {
CMLFormula formula = new CMLFormula(molecule);
molecule.appendChild(formula);
}
}
/**
* Creates new Molecule one ligand shell larger than this one. uses
* molecule.getAtomSet()
*
* @see #sprout(CMLAtomSet)
* @return new Molecule
*/
public CMLMolecule sprout() {
return sprout(this.getAtomSet());
}
// ====================== BOND ============
/**
<<<<<<< .working
=======
* get substituent ligands of one end of bond.
*
* gets all substituent atoms of atom (but not otherAtom in bond)
*
* @param bond
* @param atom at one end of bond
* @return the list of substituent atoms
*/
private static List getSubstituentLigandList(CMLBond bond, CMLAtom atom) {
CMLAtom otherAtom = bond.getOtherAtom(atom);
List substituentLigandList = new ArrayList();
List ligandList = atom.getLigandAtoms();
for (CMLAtom ligand : ligandList) {
if (!ligand.equals(otherAtom)) {
substituentLigandList.add(ligand);
}
}
return substituentLigandList;
}
/**
* gets four atoms defining cis/trans isomerism.
*
* selects 4 atoms lig0-atom0-atom1-lig1, where atom0 and atom1 are the
* atoms in the bond and lig0 and lig1 are atoms bonded to each end
* respectively Returns null unless atom0 and atom 1 each have 1 or 2
* ligands (besides themselves) if atom0 and/or atom1 have 2 ligands the
* selection is deterministic but arbitrary - hydrogens are not used if
* possible - normally the first Id in lexical order (i.e. no implied
* semantics). Exceptions are thrown if ligands are linear with atom0-atom1
* or 2 ligands on same atom are on the same side these may be normal
* exceptions (e.g. in a C=O bond) so should be caught and analysed rather
* than dumping the program
*
* @param bond
* @exception RuntimeException
* 2 ligand atoms on same atom on same side too few or too
* many ligands at either end (any) ligand is linear with
* bond
* @return the four atoms
*/
static CMLAtom[] createAtomRefs4(CMLBond bond) throws RuntimeException {
CMLAtom[] atom4 = null;
List atomList = bond.getAtoms();
List ligands0 = getSubstituentLigandList(bond, atomList.get(0));
List ligands1 = getSubstituentLigandList(bond, atomList.get(1));
if (ligands0.size() == 0) {
// no ligands on atom
} else if (ligands1.size() == 0) {
// no ligands on atom
} else if (ligands0.size() > 2) {
throw new RuntimeException("Too many ligands on atom: "
+ atomList.get(0).getId());
} else if (ligands1.size() > 2) {
throw new RuntimeException("Too many ligands on atom: "
+ atomList.get(1).getId());
} else {
CMLAtom ligand0 = ligands0.get(0);
if (AS.H.equals(ligand0.getElementType()) && ligands0.size() > 1
&& ligands0.get(1) != null) {
ligand0 = ligands0.get(1);
} else if (ligands0.size() > 1
&& ligands0.get(1).getId().compareTo(atomList.get(0).getId()) < 0) {
ligand0 = ligands0.get(1);
}
CMLAtom ligand1 = ligands1.get(0);
if (AS.H.equals(ligand1.getElementType()) && ligands1.size() > 1
&& ligands1.get(1) != null) {
ligand1 = ligands1.get(1);
} else if (ligands1.size() > 1
&& ligands1.get(1).getId().compareTo(atomList.get(1).getId()) < 0) {
ligand1 = ligands1.get(1);
}
atom4 = new CMLAtom[4];
atom4[0] = ligand0;
atom4[1] = atomList.get(0);
atom4[2] = atomList.get(1);
atom4[3] = ligand1;
}
return atom4;
}
/**
* gets four atoms defining atomParity isomerism.
* applies only to 4-coordinate atoms l1-X(l2)(l3)-l4
* and possibly 3-coordinate atoms l1-X(l2)-l3
* when central atom is added to list: l1-l2-l3-X
*
* @param atom
* @return the four atoms or null
*/
static CMLAtom[] getAtomRefs4(CMLAtom atom) throws RuntimeException {
CMLAtom[] atom4 = null;
List ligandList = atom.getLigandAtoms();
if (ligandList.size() < 3) {
} else {
atom4 = new CMLAtom[4];
atom4[0] = ligandList.get(0);
atom4[1] = ligandList.get(1);
atom4[2] = ligandList.get(2);
atom4[3] = (ligandList.size() == 3) ? atom : ligandList.get(3);
}
return atom4;
}
/**
* gets atoms on one side of bond. only applicable to acyclic bonds if bond
* is cyclic, whole molecule will be returned! returns atom and all
* descendant atoms.
*
* @param bond
* @param atom defining side of bond
* @throws RuntimeException atom is not in bond
* @return atomSet of downstream atoms
*/
public CMLAtomSet getDownstreamAtoms(CMLBond bond, CMLAtom atom) {
CMLAtomSet atomSet = new CMLAtomSet();
CMLAtom otherAtom = bond.getOtherAtom(atom);
if (otherAtom != null) {
atomSet = getDownstreamAtoms(otherAtom, atom);
}
return atomSet;
}
// ==================== MOLECULE ====================
/**
>>>>>>> .merge-right.r915
* add suffix to atom IDs.
*
* Add a distinguishing suffix to all atom IDs this allows multiple copies
* of a fragment in a molecule
*
* @param suffix
* @throws RuntimeException
*/
public void addSuffixToAtomIDs(String suffix) {
for (CMLAtom atom : molecule.getAtoms()) {
atom.renameId(atom.getId() + suffix);
}
}
/**
* Add or delete hydrogen atoms to satisy valence.
*
* does not use CDK
*
* @param control
* specifies whether H are explicit or in hydorgenCount
*/
public void adjustHydrogenCountsToValency(HydrogenControl control) {
if (molecule.isMoleculeContainer()) {
CMLElements molecules = molecule.getMoleculeElements();
for (CMLMolecule mol : molecules) {
MoleculeTool.getOrCreateTool(mol).adjustHydrogenCountsToValency(control);
}
} else {
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
AtomTool atomTool = AtomTool.getOrCreateTool(atom);
atomTool.adjustHydrogenCountsToValency(control);
}
}
}
/** Traverses all non-H atoms and contracts the hydrogens on each.
* Can control whether H with stereogenic bonds are contracted or not.
*
* @param control
* @param contractStereoH
*/
public void contractExplicitHydrogens(HydrogenControl control, boolean contractStereoH) {
if (molecule.isMoleculeContainer()) {
CMLElements molecules = molecule.getMoleculeElements();
for (CMLMolecule mol : molecules) {
MoleculeTool.getOrCreateTool(mol).contractExplicitHydrogens(control, contractStereoH);
}
} else {
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
if (!AS.H.equals(atom.getElementType())) {
if (contractStereoH) {
AtomTool.getOrCreateTool(atom).contractExplicitHydrogens(control);
} else {
boolean contract = true;
for (CMLBond bond : atom.getLigandBonds()) {
List bondStereoNodes = CMLUtil.getQueryNodes(bond, ".//cml:"+CMLBondStereo.TAG, CMLConstants.CML_XPATH);
for (Node bondStereoNode : bondStereoNodes) {
String stereo = bondStereoNode.getValue();
if (CMLBond.WEDGE.equals(stereo) || CMLBond.HATCH.equals(stereo)) {
contract = false;
}
}
}
if ("R".equals(atom.getElementType()) || "Xx".equals(atom.getElementType()) ||
atom.getChemicalElement().isChemicalElementType(Type.METAL)) {
contract = false;
}
if (contract) {
AtomTool.getOrCreateTool(atom).contractExplicitHydrogens(control);
}
}
}
}
}
}
/**
* Traverses all non-H atoms and calls CMLAtom.expandImplicitHydrogens on each.
*
* @param control as in CMLAtom
* @see CMLAtom
*/
public void expandImplicitHydrogens(HydrogenControl control) {
for (CMLAtom atom : molecule.getAtoms()) {
if (!AS.H.equals(atom.getElementType())) {
AtomTool atomTool = AtomTool.getOrCreateTool(atom);
atomTool.expandImplicitHydrogens(control);
}
}
}
/**
* removes atoms from this with identical coordinates to those in mol. uses
* deleteAtom (which may delete bonds if present)
*
* @param mol
* should be unaltered
* @param type
* of coordinate to use
*/
public void removeOverlapping3DAtoms(CMLMolecule mol, CoordinateType type) {
CMLAtomSet atomSet = AtomSetTool.getOrCreateTool(this.getAtomSet())
.getOverlapping3DAtoms(MoleculeTool.getOrCreateTool(mol).getAtomSet(), type);
for (CMLAtom atom : atomSet.getAtoms()) {
molecule.deleteAtom(atom);
}
}
/**
*
*/
public void create2DCoordinatesFrom3D(double bondLengthScale) {
List atomList = molecule.getAtoms();
for (CMLAtom atom : atomList) {
double x3 = atom.getX3();
atom.setX2(x3*bondLengthScale);
double y3 = atom.getY3();
atom.setY2(y3*bondLengthScale);
}
Real2 centroid2 = this.getAtomSet().getCentroid2D();
molecule.translate2D(centroid2.multiplyBy(-1.0));
}
/**
* gets atomset corresponding to bondset.
*
* @param bondSet
* @return the atomset
*/
public CMLAtomSet getAtomSet(CMLBondSet bondSet) {
CMLAtomSet atomSet = new CMLAtomSet();
List bonds = bondSet.getBonds();
for (CMLBond bond : bonds) {
CMLAtom atom0 = bond.getAtom(0);
CMLAtom atom1 = bond.getAtom(1);
atomSet.addAtom(atom0);
atomSet.addAtom(atom1);
}
return atomSet;
}
/**
* calculate bondorders from coordinates if present.
*
* uses Pauling bond order/length formula. use with extreme care! many bonds
* will have fractional orders and these will be difficult to manage
*/
public void calculateBondOrdersFromXYZ3() {
for (CMLBond bond : molecule.getBonds()) {
String order = null;
try {
order = bond.getOrder();
} catch (Exception e) {
LOG.error("BUG " + e);
}
// order not available?
if (order == null || order.equals(CMLBond.UNKNOWN_ORDER)) {
CMLAtom at0 = bond.getAtom(0);
if (at0 == null) {
throw new RuntimeException("NULL atom");
}
String elType = at0.getElementType();
if (elType == null) {
throw new RuntimeException("missing elementType");
}
ChemicalElement el0 = ChemicalElement
.getChemicalElement(elType);
if (el0 == null) {
continue;
}
double r0 = el0.getCovalentRadius();
CMLAtom at1 = bond.getAtom(1);
ChemicalElement el1 = ChemicalElement.getChemicalElement(at1
.getElementType());
if (el1 == null) {
continue;
}
double r1 = el1.getCovalentRadius();
double dlen = at0.getDistanceTo(at1);
if (dlen < 0.0) {
continue;
}
double paulingBO = Math.exp(2.303 * (-dlen + r0 + r1) / 0.7);
try {
if (paulingBO < 1.3) {
bond.setOrder(CMLBond.SINGLE_S);
} else if (paulingBO < 1.75) {
bond.setOrder(CMLBond.AROMATIC);
} else if (paulingBO < 2.5) {
bond.setOrder(CMLBond.DOUBLE_D);
} else if (paulingBO < 5) {
bond.setOrder(CMLBond.TRIPLE_T);
}
} catch (Exception e) {
Util.BUG(e);
}
}
}
}
/**
* calculates which atoms are bonded by covalent radius criterion.
*
*/
public void calculateBondedAtoms() {
for (CMLMolecule mol : molecule.getDescendantsOrMolecule()) {
List atoms = mol.getAtoms();
// need to remove old bonds first
for (CMLBond bond : mol.getBonds()) {
bond.detach();
}
CMLBondArray ba = mol.getBondArray();
if (ba != null) {
ba.indexBonds();
}
calculateBondedAtoms(atoms, mol);
}
}
/**
* @param atoms
*/
public void calculateBondedAtoms(List atoms) {
CMLMolecule mol = null;
for (CMLAtom atom : atoms) {
CMLMolecule m = atom.getMolecule();
if (mol != null) {
if (mol != atom.getMolecule()) {
throw new RuntimeException("All CMLAtoms must belong to the same CMLMolecule");
}
}
mol = m;
}
calculateBondedAtoms(atoms, mol);
}
private void calculateBondedAtoms(List atoms, CMLMolecule molecule) {
for (int i = 0; i < atoms.size(); i++) {
CMLAtom atomi = (CMLAtom) atoms.get(i);
for (int j = i + 1; j < atoms.size(); ++j) {
CMLAtom atomj = (CMLAtom) atoms.get(j);
if (CMLBond.areWithinBondingDistance(atomi, atomj)) {
molecule.addBond(new CMLBond(atomi, atomj));
}
}
}
}
/**
* calculate all bonds from this to molecule and join. will transfer all
* atoms from molecule to this except overlapping ones, thereby corrupting
* molecule. Overlapping atoms are discarded
*
* @param molecule2
* to join (will be corrupted)
* @return true if 1 or more bonds were made
*/
boolean calculateBondsToAndJoin(CMLMolecule molecule2) {
List atoms = molecule.getAtoms();
List atoms2 = molecule2.getAtoms();
boolean madeBond = false;
int i = 0;
int idMax = this.getMaximumId("a");
for (CMLAtom atomi : atoms) {
int j = 0;
for (CMLAtom atomj : atoms2) {
if (CMLBond
.areWithinBondingDistance(atomi, atomj)) {
madeBond = true;
if (atomi.getDistanceTo(atomj) < 0.2) {
// remove overlapping atoms
atomj.detach();
//LOG.debug("OVERLAP........................... "
// + atomj.getId());
} else {
// transfer atom and add new bond
atomj.detach();
String id = "a" + (++idMax);
atomj.setId(id);
molecule.addAtom(atomj);
molecule.addBond(new CMLBond(atomi, atomj));
}
}
j++;
}
i++;
}
if (madeBond) {
// transfer rest of atoms from molecule 2
idMax = this.getMaximumId("a");
atoms2 = molecule2.getAtoms();
for (CMLAtom atom2 : atoms2) {
atom2.detach();
String id = "a" + (++idMax);
atom2.setId(id);
molecule.addAtom(atom2);
}
calculateBondedAtoms(atoms2, molecule);
}
return madeBond;
}
/**
* iterates through atom ids and finds the largest numerically. assumes they
* are of form: "a123" and returns largest value of integer
*
* @param prefix
* (e.g. "a")
* @return largest integer following prefix
*/
private int getMaximumId(String prefix) {
int max = -1;
int l = prefix.length();
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
String id = atom.getId();
try {
int ii = Integer.parseInt(id.substring(l));
max = (max < ii) ? ii : max;
} catch (NumberFormatException e) {
;//
}
}
return max;
}
/**
* Generates and adds unique bond ids.
*
* Uses CMLBond.generateId.
*/
public void generateBondIds() {
for (CMLBond bond : molecule.getBonds()) {
bond.generateAndSetId();
}
}
/**
* get the average atom-atom bond distance.
*
* This is primarily for scaling purposes and should not be taken too
* seriously.
*
* @param type
* @param omitHydrogens coordinates
* @return average distance in origianl units. If no bonds returns negative.
*/
public double getAverageBondLength(CoordinateType type, boolean omitHydrogens) {
double bondSum = 0.0;
int nBonds = 0;
for (CMLBond bond : molecule.getBonds()) {
if (omitHydrogens && (
"H".equals(bond.getAtom(0).getElementType()) ||
"H".equals(bond.getAtom(1).getElementType()))
) {
continue;
}
try {
double length = bond.calculateBondLength(type);
if (!Double.isNaN(length)) {
LOG.trace("len "+length);
bondSum += length;
nBonds++;
}
} catch (RuntimeException e) {
// no coordinates
}
}
return (nBonds == 0 || Double.isNaN(bondSum) || Real.isZero(bondSum, Real.EPS))
? Double.NaN : bondSum / ((double) nBonds);
}
/**
* get the average atom-atom bond distance.
*
* This is primarily for scaling purposes and should not be taken too
* seriously.
*
* @param type
* @param omitHydrogens coordinates
* @return average distance in origianl units. If no bonds returns negative.
*/
public void scaleAverage2DBondLength(double len) {
boolean omitHydrogens = true;
double averageBondLength = this.getAverageBondLength(CoordinateType.TWOD , omitHydrogens);
double scale = len/averageBondLength;
molecule.multiply2DCoordsBy(scale);
}
/**
* get the average atom-atom bond distance.
*
* This is primarily for scaling purposes and should not be taken too
* seriously.
*
* @param type
* @return average distance in origianl units. If no bonds returns negative.
*/
public double getAverageBondLength(CoordinateType type) {
return getAverageBondLength(type, false);
}
/**
* get matched bond using atom mapping. does not work with sets
*
* if bond atomRefs2="a1 a2" and link to="a1" from="b1" // atomId and link
* to="a2" from="b2" // atomId and toFrom = Direction.FROM then will return
* bond atomRefs2="b1 b2" or atomRefs2="b2 b1" in molecule1
*
* @param bond0 bond to search with. the values in must occur in a single
* toFrom attribute
* @param map with links
* @param toFrom specifies attribute for search atoms in atomRefs2
* @return mapped bond or null
*/
public CMLBond getMappedBondViaAtoms(CMLMap map, CMLBond bond0,
Direction toFrom) {
CMLBond targetBond = null;
CMLAtom atom0 = bond0.getAtom(0);
CMLAtom atom1 = bond0.getAtom(1);
if (atom0 != null && atom1 != null) {
String targetRef0 = map.getRef(atom0.getId(), toFrom);
String targetRef1 = map.getRef(atom1.getId(), toFrom);
targetBond = molecule.getBond(molecule.getAtomById(targetRef0),
molecule.getAtomById(targetRef1));
}
return targetBond;
}
/**
* make sub molecule.
*
* @param molecule
* @param atomIds
* @return molecule
*
*/
public static CMLMolecule createMolecule(CMLMolecule molecule,
String[] atomIds) {
CMLMolecule newMolecule = new CMLMolecule();
for (String atomId : atomIds) {
CMLAtom atom = new CMLAtom(atomId);
newMolecule.addAtom(atom);
}
return newMolecule;
}
public CMLAtomSet createAtomSet(int[] serials) {
CMLAtomSet atomSet = new CMLAtomSet();
for (int serial : serials) {
CMLAtom atom = molecule.getAtom(serial);
if (atom != null && !atomSet.contains(atom)) {
atomSet.addAtom(atom);
}
}
return atomSet;
}
/**
* creates bonds and partitions molecules and returns contacts. currently
* looks only for homo-molecule contacts.
*
* @param dist2Range
* @param crystalTool
* @return sorted contact list
*/
public List getSymmetryContacts(RealRange dist2Range,
CrystalTool crystalTool) {
this.createCartesiansFromFractionals(crystalTool.getCrystal());
this.calculateBondedAtoms();
List contactList = crystalTool.getSymmetryContactsToMolecule(dist2Range);
Collections.sort(contactList);
return contactList;
}
/**
* convenience method to get molecule or its child molecules as a list.
*
* @return list of either just the molecule (if no children) or list of the
* children
*/
public List getMoleculeList() {
List moleculeList = molecule.getDescendantsOrMolecule();
return moleculeList;
}
/**
* get linkers
*
* @return linkerMolecules
*/
public List getChainMolecules() {
List linkers = new ArrayList();
List subMolList = molecule.getDescendantsOrMolecule();
for (CMLMolecule subMol : subMolList) {
ConnectionTableTool ct = new ConnectionTableTool(subMol);
List acyclicBonds = ct.getAcyclicBonds();
List cyclicBonds = ct.getCyclicBonds();
List acyclicBondAtoms = new ArrayList();
List cyclicBondAtoms = new ArrayList();
for (CMLBond acyclicBond : acyclicBonds) {
for (CMLAtom atom : acyclicBond.getAtoms()) {
acyclicBondAtoms.add(atom);
}
}
for (CMLBond cyclicBond : cyclicBonds) {
for (CMLAtom atom : cyclicBond.getAtoms()) {
cyclicBondAtoms.add(atom);
}
}
// if bond count equal to number of acyclic bonds then there
// aren't any cyclic bonds in the molecule, hence no linkers
if (subMol.getBondCount() != acyclicBonds.size()) {
List acyclicAtoms = new ArrayList();
for (CMLAtom atom : subMol.getAtoms()) {
if (acyclicBondAtoms.contains(atom) &&
!cyclicBondAtoms.contains(atom)) {
acyclicAtoms.add(atom);
}
}
CMLAtomSet atomSet = CMLAtomSet.createFromAtoms(acyclicAtoms);
CMLMolecule newMol = MoleculeTool.createMolecule(atomSet);
MoleculeTool.getOrCreateTool(newMol).calculateBondedAtoms();
new ConnectionTableTool(newMol).partitionIntoMolecules();
List linkerList = newMol.getDescendantsOrMolecule();
for (CMLMolecule mol : linkerList) {
if (mol.getAtomCount() > 1) {
linkers.add(mol);
}
}
}
}
return linkers;
}
/**
* get ring nuclei. calls connectionTableTool.getRingNucleiMolecules
*
* @return ringNucleiMolecules
*/
public List getRingNucleiMolecules() {
ConnectionTableTool connectionTableTool =
// not sure whether we have to clone the molecule
// new ConnectionTableTool(new CMLMolecule(subMolecule));
new ConnectionTableTool(molecule);
return connectionTableTool.getRingNucleiMolecules();
}
/**
* join one molecule to another.
* manages the XML but not yet the geometry
* empties the added molecule of elements and copies them
* to this.molecule and then detaches the addedMolecule
* @param addedMolecule to be joined
* @param takeAtomWithLowestId DUMMY AT PRESENT
*/
public void addMoleculeTo(CMLMolecule addedMolecule, boolean takeAtomWithLowestId) {
addMoleculeTo(addedMolecule);
}
/** add atoms.
* calls addAtom
* @param atomSet
* @throws CMLRuntimeException
* null, non-uniqueID, etc.
*/
public void addAtoms(CMLAtomSet atomSet) {
for (CMLAtom atom : atomSet.getAtoms()) {
molecule.addAtom(atom);
}
}
/** add bonds.
* calls addBond
* @param bondSet
* @throws RuntimeException
* null, non-uniqueID, etc.
*/
public void addBonds(CMLBondSet bondSet) {
for (CMLBond bond : bondSet.getBonds()) {
molecule.addBond(bond);
}
}
/**
* join one molecule to another.
* manages the XML but not yet the geometry
* empties the added molecule of elements and copies them
* to this.molecule and then detaches the addedMolecule
* @param addedMolecule to be joined
*/
public void addMoleculeTo(CMLMolecule addedMolecule) {
MoleculeTool addedMoleculeTool = MoleculeTool.getOrCreateTool(addedMolecule);
addedMoleculeTool.renumberToUniqueIds(this.getAtomIdList());
molecule.getOrCreateAtomArray();
// bonds must be done first as atom.detach() destroys bonds
List bondList = addedMolecule.getBonds();
for (CMLBond bond : bondList) {
bond.detach();
molecule.addBond(bond);
}
List atomList = addedMolecule.getAtoms();
for (CMLAtom atom : atomList) {
atom.detach();
molecule.addAtom(atom);
}
// copy any remaining nodes except atomArray and bondArray
Nodes nodes = addedMolecule.query(".//*", CMLConstants.CML_XPATH);
int nnodes = nodes.size();
for (int i = nnodes - 1; i >= 0; i--) {
Node node = nodes.get(i);
node.detach();
if (node instanceof CMLAtomArray) {
// don't copy
} else if (node instanceof CMLBondArray) {
// don't copy
} else {
molecule.appendChild(node);
}
}
addedMolecule.detach();
}
/**
* join one molecule to another.
* manages the XML but not yet the geometry
* empties the added molecule of elements and copies them
* to this.molecule and then detaches the addedMolecule
* @param addedMolecule to be joined
*/
public void addMoleculesTo(List addedMolecules) {
if (addedMolecules != null) {
for (CMLMolecule addedMolecule : addedMolecules) {
this.addMoleculeTo(addedMolecule);
}
}
}
/**
* @return list of atomIds in order
*/
public List getAtomIdList() {
List idList = new ArrayList();
for (CMLAtom atom : molecule.getAtoms()) {
idList.add(atom.getId());
}
return idList;
}
/**
* renumbers atoms in molecule so they do not clash with other Ids
* iterates through "a1", "a2" finding the free spaces.
* hydrogens will not be prettily numbered
* @param avoidList ids already alocated in other molecules
*/
public void renumberToUniqueIds(List avoidList) {
Set stringSet = checkUnique(avoidList);
int totalSize = molecule.getAtomCount() + avoidList.size();
List fromIdList = new ArrayList();
List toIdList = new ArrayList();
// TODO hydrogens will not be pretty -
int ii = 1;
for (CMLAtom atom : molecule.getAtoms()) {
String id = atom.getId();
if (stringSet.contains(id)) {
fromIdList.add(id);
String newId = null;
while (ii < totalSize) {
newId = "a"+ii;
if (!stringSet.contains(newId) && molecule.getAtomById(newId) == null) {
stringSet.add(newId);
// new unique
break;
}
ii++;
}
toIdList.add(newId);
LOG.trace(id+" => "+newId);
}
}
renumberAtomIds(fromIdList, toIdList);
}
/** adjust the cartesians to fit declared torsions.
*/
public void adjustTorsions() {
List torsions = this.getTorsionElements();
for (CMLTorsion torsion : torsions) {
TorsionTool.getOrCreateTool(torsion).adjustCoordinates(molecule);
}
}
/** adjust the cartesians to fit declared angles.
*/
public void adjustAngles() {
List angles = this.getAngleElements();
for (CMLAngle angle : angles) {
AngleTool.getOrCreateTool(angle).adjustCoordinates(molecule);
}
}
/** adjust the cartesians to fit declared lengths.
*/
public void adjustLengths() {
List lengths = this.getLengthElements();
for (CMLLength length : lengths) {
LengthTool lengthTool = LengthTool.getOrCreateTool(length);
lengthTool.adjustCoordinates(molecule);
}
}
/** iterates through torsions in molecule.
* if they contain atom0 and atom1, adjusts them to value of torsion
* @param atom0
* @param atom1
*/
public void adjustTorsions(CMLAtom atom0, CMLAtom atom1) {
if (molecule.hasCoordinates(CoordinateType.CARTESIAN)) {
// set torsions
CMLAtomSet moleculeAtomSet = this.getAtomSet();
Nodes torsions = molecule.query(".//"+CMLTorsion.NS, CMLConstants.CML_XPATH);
int nTors = torsions.size();
for (int i = 0; i < nTors; i++) {
CMLTorsion torsion = (CMLTorsion) torsions.get(i);
TorsionTool torsionTool = TorsionTool.getOrCreateTool(torsion);
String[] atomRefs4 = torsion.getAtomRefs4();
if (atomRefs4 != null
&& (atom0.getId().equals(atomRefs4[1]) && atom1.getId().equals(atomRefs4[2]) ||
atom0.getId().equals(atomRefs4[2]) && atom1.getId().equals(atomRefs4[1]))
) {
double d = Double.NaN;
try {
d = torsion.getXMLContent();
} catch (RuntimeException e) {
// no value given
continue;
// empty torsion;
}
CMLAtomSet moveableAtomSet =
AtomTool.getOrCreateTool(atom1).getDownstreamAtoms(atom0);
torsionTool.adjustCoordinates(new Angle(d, Angle.Units.DEGREES),
moleculeAtomSet, moveableAtomSet);
// this is to avoid the torsion being reused
torsion.removeAttribute("atomRefs4");
}
}
}
}
public void adjustHydrogenLengths(double factor) {
LOG.debug("hlen factor "+factor);
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
if ("H".equals(atom.getElementType())) {
Real2 xy = atom.getXY2();
if (xy != null) {
List ligands = atom.getLigandAtoms();
if (ligands.size() == 1) {
Real2 xyLig = ligands.get(0).getXY2();
Real2 vector = xy.subtract(xyLig);
vector = vector.multiplyBy(factor);
Real2 xy1 = xyLig.plus(vector);
atom.setXY2(xy1);
}
}
}
}
}
/** flatten molecules.
*
* @param parent
*/
public void flattenMoleculeDescendants(ParentNode parent) {
Elements moleculeLists = molecule.getChildCMLElements(CMLMoleculeList.TAG);
List moleculeListList = new ArrayList();
for (int i = 0; i < moleculeLists.size(); i++) {
moleculeListList.add((CMLMoleculeList) moleculeLists.get(i));
}
for (CMLMoleculeList moleculeList : moleculeListList) {
flattenMoleculeListDescendants(moleculeList, molecule);
if (parent != null) {
moleculeList.detach();
} else {
CMLUtil.transferChildren(moleculeList, molecule);
moleculeList.detach();
}
}
}
private void flattenMoleculeListDescendants(
CMLMoleculeList moleculeList, CMLMolecule parentMolecule) {
ParentNode grandParent = parentMolecule.getParent();
int idx = grandParent.indexOf(parentMolecule);
// moleculeList.debug("MOLL");
Elements childs = moleculeList.getChildElements();
for (int i = 0; i < childs.size(); i++) {
Node child = childs.get(i);
if (child instanceof Text) {
child.detach();
} else {
if (grandParent instanceof CMLElement) {
child.detach();
grandParent.insertChild(child, ++idx);
}
if (child instanceof CMLMolecule) {
MoleculeTool childTool = MoleculeTool.getOrCreateTool((CMLMolecule) child);
childTool.flattenMoleculeDescendants(moleculeList);
}
}
}
}
/** copies attributes on bonds and atoms to another molecule.
* for each atom/bond in this.molecule finds Id and hence corresponding
* atom/bond in 'to'. Copies all attributes from that atom to to.atom/@*
* If corresponding atom does not exist, throws exception.
* If target attribute exists throws exception
* @param to
* @param permitOverwrite allow existing attributes on target to be overwritten
* @exception RuntimeException ids in molecules do not correspond or
* attributes are already present
*/
public void copyAtomAndBondAttributesById(CMLMolecule to, boolean permitOverwrite) {
copyAtomAttributesById(to, permitOverwrite);
copyBondAttributesById(to, permitOverwrite);
}
/** copies attributes on atoms to another molecule.
* for each atom in this.molecule finds Id and hence corresponding
* atom in 'to'. Copies all attributes from that atom to to.atom/@*
* If corresponding atom does not exist, throws exception.
* If target attribute exists throws exception
* @param to
* @param permitOverwrite allow existing attributes on target to be overwritten
* @exception RuntimeException ids in molecules do not correspond or
* attributes are already present
*/
public void copyAtomAttributesById(CMLMolecule to, boolean permitOverwrite) {
List fromAtoms = molecule.getAtoms();
for (CMLAtom fromAtom : fromAtoms) {
String fromId = fromAtom.getId();
if (fromId != null) {
CMLAtom toAtom = to.getAtomById(fromId);
if (toAtom == null) {
throw new RuntimeException("Cannot find target atom: "+fromId);
}
copyAttributes(fromAtom, toAtom, permitOverwrite);
}
}
}
/** copies attributes on bonds to another molecule.
* for each bond in this.molecule finds Id and hence corresponding
* bond in 'to'. Copies all attributes from that bond to to.bond/@*
* If corresponding bond does not exist, throws exception.
* If target attribute exists throws exception
* @param to
* @param permitOverwrite allow existing attributes on target to be overwritten
* @exception RuntimeException ids in molecules do not correspond or
* attributes are already present
*/
public void copyBondAttributesById(CMLMolecule to, boolean permitOverwrite) {
List fromBonds = molecule.getBonds();
for (CMLBond fromBond : fromBonds) {
String fromId = fromBond.getId();
if (fromId != null) {
CMLBond toBond = to.getBondById(fromId);
if (toBond == null) {
throw new RuntimeException("Cannot find target bond: "+fromId);
}
copyAttributes(fromBond, toBond, permitOverwrite);
}
}
}
private void copyAttributes(Element from, Element to, boolean permitOverwrite) {
List nodes = CMLUtil.getQueryNodes(from, "./@*");
for (Node node : nodes) {
Attribute attribute = (Attribute) node;
String name = attribute.getLocalName();
if ("id".equals(name)) {
continue;
}
if (!permitOverwrite && to.getAttribute(name) != null) {
throw new RuntimeException("cannot overwrite attribute: "+name);
}
Attribute newAttribute = new Attribute(name, attribute.getValue());
to.addAttribute(newAttribute);
}
}
/**
* @param type
* @param contactDist
* @return pairs of atoms which bump
*/
public List getBumps(CoordinateType type, double contactDist) {
List bumpList = new ArrayList();
List atomList = molecule.getAtoms();
CMLAtomSet allAtomSet = new CMLAtomSet(molecule);
int natoms = atomList.size();
for (int i = 0; i < natoms; i++) {
CMLAtom atomi = atomList.get(i);
if (AS.H.equals(atomi.getElementType())) {
continue;
}
// get all atoms within 3 bonds
CMLAtomSet atomSet13 = AtomTool.getOrCreateTool(atomi).getCoordinationSphereSet(3);
CMLAtomSet nonBonded = allAtomSet.complement(atomSet13);
List nonBondedAtomList = nonBonded.getAtoms();
for (CMLAtom atomj : nonBondedAtomList) {
if (AS.H.equals(atomj.getElementType())) {
continue;
}
if (atomi.getId().compareTo(atomj.getId()) <= 0) {
continue;
}
boolean bump = false;
double dist = Double.NaN;
if (type == CoordinateType.CARTESIAN) {
bump = atomi.isWithinRadiusSum(atomj, ChemicalElement.RadiusType.VDW);
dist = atomi.getDistanceTo(atomj);
} else if (type == CoordinateType.TWOD) {
dist = atomi.getDistance2(atomj);
if (dist < contactDist) {
bump = true;
}
}
if (bump) {
AtomPair atomPair = new AtomPair(atomi, atomj);
atomPair.setDistance(dist, type);
bumpList.add(atomPair);
}
}
}
return bumpList;
}
/**
*
* @param molecule
* @return
*/
public static AbstractSVGTool getOrCreateSVGTool(CMLMolecule molecule) {
return (AbstractSVGTool) MoleculeTool.getOrCreateTool(molecule);
}
/** returns a "g" element
* will require to be added to an svg element
* @param drawable
* @throws IOException
* @return null if problem
*/
public SVGElement createGraphicsElement(CMLDrawable drawable) {
SVGElement g = null;
enableMoleculeDisplay();
Transform2 transform2 = new Transform2(
new double[] {
1., 0., 0.0,
0., -1., 0.0,
0., 0., 1.}
);
List atoms = molecule.getAtoms();
List bonds = molecule.getBonds();
setAtomBondAndGroupVisibility(atoms, bonds);
if (atoms.size() == 0) {
g = this.getCascadingFormulaOrTextSVGElement();
} else if (atoms.size() == 1) {
g = this.getCascadingFormulaOrTextSVGElement();
} else {
g = drawMultiAtomMolecule(drawable, g, transform2);
}
return g;
}
private SVGElement getCascadingFormulaOrTextSVGElement() {
SVGElement gg = getCascadingFormulaSVGElement();
if (gg == null) {
gg = getCascadingTextSVGElement();
}
return gg;
}
private SVGG getCascadingTextSVGElement() {
String text = getCascadingTextElement();
if (text == null) {
text = "Cannot create meaningful text";
}
return createTextWithBoundingBox(text);
}
private SVGGBox createTextWithBoundingBox(String text) {
SVGText textSVGElement = new SVGText(new Real2(0.0, 0.0), text);
textSVGElement.setFontSize(10.0);
SVGRect rect = textSVGElement.getBoundingSVGRect();
SVGGBox box = new SVGGBox();
box.appendChild(textSVGElement);
box.appendChild(rect);
return box;
}
private String getCascadingTextElement() {
String text = null;
CMLName name = getCascadingName();
if (name != null) {
text = name.getValue();
}
if (text == null) {
CMLLabel label = getCascadingLabel();
if (label != null) {
text = label.getCMLValue();
}
}
if (text == null) {
text = molecule.getTitle();
}
if (text == null) {
text = molecule.getId();
}
return text;
}
private CMLLabel getCascadingLabel() {
// fixme - maybe use more complex strategy
CMLElements labels = molecule.getLabelElements();
return (labels.size() == 0) ? null : (CMLLabel) labels.get(0);
}
private CMLName getCascadingName() {
// fixme - maybe use more complex strategy
CMLElements names = molecule.getNameElements();
return (names.size() == 0) ? null : (CMLName) names.get(0);
}
private SVGElement getCascadingFormulaSVGElement() {
String formulaS = null;
CMLElements formulas = molecule.getFormulaElements();
if (formulas.size() > 0) {
for (CMLFormula formula : formulas) {
formulaS = formula.getConcise();
if (formulaS == null) {
formulaS = formula.getInline();
}
if (formulas != null) {
break;
}
}
} else {
CMLFormula formula = new CMLFormula(molecule);
formulaS = formula.getConcise();
}
SVGElement textSVGElement = null;
if (formulaS != null) {
textSVGElement = createTextWithBoundingBox(formulaS);
}
return textSVGElement;
}
private SVGElement drawMultiAtomMolecule(CMLDrawable drawable,
SVGElement g, Transform2 transform2) {
if (applyScale) {
transform2 = scaleToBoundingBoxesAndScreenLimits(transform2);
}
if(drawable instanceof MoleculeDisplayList) {
g = ((MoleculeDisplayList) drawable).getSvg();
}
if (g == null) {
g = createSVGElement(drawable, transform2);
g.setProperties(moleculeDisplay);
}
double avlength = MoleculeTool.getOrCreateTool(molecule).getAverageBondLength(CoordinateType.TWOD);
BondDisplay defaultBondDisplay = moleculeDisplay.getDefaultBondDisplay();
defaultBondDisplay.setScale(avlength);
displayBonds(drawable, g, defaultBondDisplay);
displayAtoms(drawable, g, avlength);
ensureBoundingRect();
displayFormula(drawable, g);
displayMoleculeLabels(drawable, g);
displayMoleculeNames(drawable, g);
if (moleculeDisplay.isDrawBoundingBox()) {
drawBoundingBox(drawable, g);
scaleToScreen(g);
}
if (drawable != null) {
try {
drawable.output(g);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return g;
}
private void scaleToScreen(SVGElement g) {
if (molecule.getMoleculeElements().size() == 0) {
if (moleculeDisplay.getMaxScreenSize() != null ||
moleculeDisplay.getMinScreenSize() != null) {
double scale = 1.0;
scale = scaleToMaximumScreen(scale);
scale = scaleToMinimumScreen(scale);
Transform2 t2 = g.getTransform2FromAttribute();
t2.multiplyBy(scale);
g.setTransform(t2);
}
}
}
private double scaleToMaximumScreen(double scale) {
Real2 maxScreenSize = moleculeDisplay.getMaxScreenSize();
if (maxScreenSize != null) {
Real2 scales = getScales(maxScreenSize);
if (scales != null) {
double scalex = Math.min(scale, scales.getX());
double scaley = Math.min(scale, scales.getY());
scale = Math.min(scale, Math.min(scalex, scaley));
}
}
return scale;
}
private double scaleToMinimumScreen(double scale) {
Real2 minScreenSize = moleculeDisplay.getMinScreenSize();
if (minScreenSize != null) {
Real2 scales = getScales(minScreenSize);
if (scales != null) {
double scalex = Math.max(scale, scales.getX());
double scaley = Math.max(scale, scales.getY());
scale = Math.max(scale, Math.max(scalex, scaley));
}
}
return scale;
}
private Real2 getScales(Real2 screenSize) {
Real2 scales = null;
if (boundingRectSVG != null) {
scales = new Real2();
scales.x = screenSize.x / boundingRectSVG.getWidth();
scales.y = screenSize.y / boundingRectSVG.getHeight();
}
return scales;
}
private void displayAtoms(CMLDrawable drawable, SVGElement g,
double avlength) {
AtomDisplay defaultAtomDisplay = moleculeDisplay.getDefaultAtomDisplay();
defaultAtomDisplay.setScale(avlength);
if (molecule.getAtomCount() == 1) {
defaultAtomDisplay.setDisplayCarbons(true);
}
displayAtoms(drawable, g, defaultAtomDisplay);
}
private void drawBoundingBox(CMLDrawable drawable, SVGElement g) {
ensureBoundingRect();
if (boundingRectSVG != null) {
boundingRectSVG.setStroke(moleculeDisplay.getBoundingBoxBundle().getStroke());
boundingRectSVG.setStrokeWidth(moleculeDisplay.getBoundingBoxBundle().getStrokeWidth());
boundingRectSVG.setStrokeWidth(moleculeDisplay.getBoundingBoxBundle().getOpacity());
appendNonNullChild(g, boundingRectSVG);
}
}
private void ensureBoundingRect() {
calculateBoundingBox2D();
if (userBoundingBox != null && userBoundingBox.getXRange() != null && userBoundingBox.getYRange() != null) {
double bondLength = moleculeDisplay.getBondLength();
Real2 xy0 = new Real2(
userBoundingBox.getXRange().getMin() - bondLength,
userBoundingBox.getYRange().getMin() - bondLength
);
Real2 xy1 = new Real2(
userBoundingBox.getXRange().getMax() + bondLength,
userBoundingBox.getYRange().getMax() + bondLength
);
boundingRectSVG = new SVGRect(xy0, xy1);
}
}
private void setAtomBondAndGroupVisibility(
List atoms, List bonds) {
if (/*true || */
Level.DEBUG.equals(LOG.getLevel())) {
molecule.debug("GROUP VISIBILITY ON");
}
// add atomDisplay to each atomTool
for (CMLAtom atom : atoms) {
AtomTool atomTool = AtomTool.getOrCreateTool(atom);
// FIXME PMR
atomTool.ensureAtomDisplay();
atomTool.getAtomDisplay().setMoleculeDisplay(moleculeDisplay);
// group visibility (NYI)
if (atomTool.isGroupRoot()) {
atomTool.ensureAtomDisplay();
atomTool.setDisplay(false);
}
}
// add bondDisplay to each bondTool
for (CMLBond bond : bonds) {
BondTool bondTool = BondTool.getOrCreateTool(bond);
bondTool.ensureBondDisplay();
bondTool.getBondDisplay().setMoleculeDisplay(moleculeDisplay);
}
}
private Transform2 scaleToBoundingBoxesAndScreenLimits(Transform2 transform2) {
try {
calculateBoundingBox2D();
// Real2Range moleculeBoundingBox = AtomSetTool.getOrCreateTool(
// new CMLAtomSet(molecule)).getExtent2();
Real2Interval screenBoundingBox = moleculeDisplay.getScreenExtent();
// Real2Interval moleculeInterval = new Real2Interval(moleculeBoundingBox);
Real2Interval moleculeInterval = new Real2Interval(userBoundingBox);
double scale = moleculeInterval.scaleTo(screenBoundingBox);
double[] offsets = moleculeInterval.offsetsTo(screenBoundingBox, scale);
transform2 = new Transform2 (
new double[] {
scale, 0., offsets[0],
0.,-scale, offsets[1],
0., 0., 1.}
);
} catch (NullPointerException npe) {
// happens with small number of atoms
}
return transform2;
}
public Real2Range calculateBoundingBox2D() {
userBoundingBox = null;
if (molecule != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(new CMLAtomSet(molecule));
userBoundingBox = (atomSetTool == null) ? null : atomSetTool.getExtent2();
}
return userBoundingBox;
}
public Real3Range calculateBoundingBox3D() {
Real3Range userBoundingBox3 = null;
if (molecule != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(new CMLAtomSet(molecule));
userBoundingBox3 = (atomSetTool == null) ? null : atomSetTool.getExtent3();
}
return userBoundingBox3;
}
private void displayAtoms(CMLDrawable drawable, SVGElement g, AtomDisplay atomDisplay) {
for (CMLAtom atom : molecule.getAtoms()) {
AtomTool atomTool = getOrCreateAtomTool(atom);
atomTool.ensureAtomDisplay();
atomTool.getAtomDisplay().setMoleculeDisplay(moleculeDisplay);
atomTool.setMoleculeTool(this);
if (atomTool.getAtomDisplay().omitAtom(atom)) {
continue;
}
GraphicsElement atomSvg = atomTool.createGraphicsElement(drawable);
appendNonNullChild(g, atomSvg);
}
}
private void displayBonds(
CMLDrawable drawable, SVGElement g,
BondDisplay bondDisplay) {
for (CMLBond bond : molecule.getBonds()) {
BondTool bondTool = getOrCreateBondTool(bond);
bondTool.setBondDisplay(bondDisplay);
bondTool.setMoleculeTool(this);
if (!bondTool.getBondDisplay().omitBond(bond)) {
SVGElement bondSvg = bondTool.createGraphicsElement(drawable);
appendNonNullChild(g, bondSvg);
}
}
}
private void displayMoleculeNames(CMLDrawable drawable, SVGElement g) {
if (moleculeDisplay.isDisplayNames()) {
// Real2 displayPoisition = moleculeDisplay.getNameXPosition()
double x = 50.;
double y = 0.;
// double y = 50.;
double deltay = 20.0;
int i = 0;
Transform2 transform2 = new Transform2(
new double[] {
1., 0., 0.0,
0., -1., 0.0,
0., 0., 1.}
);
for (CMLName name : molecule.getNameElements()) {
SVGText text = new SVGText(new Real2(x, y+(i*deltay)), name.getValue());
if (moleculeDisplay.getLabelFontSize() != null) {
text.setFontSize(moleculeDisplay.getLabelFontSize());
}
text.setTransform(transform2);
appendNonNullChild(g, text);
i++;
}
}
}
private void displayMoleculeLabels(CMLDrawable drawable, SVGElement g) {
if (moleculeDisplay.isDisplayLabels()) {
calculateBoundingBox2D();
RealRange xRange = userBoundingBox.getXRange();
if (xRange != null) {
double xMid = xRange.getMidPoint();
double maxY = userBoundingBox.getYRange().getMax();
Transform2 transform2 = new Transform2(
new double[] {
1., 0., 0.0,
0., -1., 0.0,
0., 0., 1.}
);
if (molecule.getLabelElements().size() > 0) {
displayLabels(g, xMid, maxY, transform2);
}
}
}
}
private void displayLabels(SVGElement g, double xMid, double maxY,
Transform2 transform2) {
double labelFontSize = moleculeDisplay.getLabelFontSize();
double labelYSpacing = moleculeDisplay.getLabelYSpacing();
// double yLabel0 = maxY + labelYSpacing * labelFontSize;
double yLabel0 = maxY;
for (CMLLabel label : molecule.getLabelElements()) {
String labelS = label.getCMLValue();
double xLabel = xMid - labelFontSize * (labelS.length() / 2.);
double yLabel = labelYSpacing * labelFontSize;
SVGText text = new SVGText(new Real2(xLabel, yLabel0 + yLabel), labelS);
text.setFontWeight("bold");
text.setFontSize(labelFontSize);
text.setTransform(transform2);
appendNonNullChild(g, text);
}
}
private void displayFormula(CMLDrawable drawable, SVGElement g) {
if (moleculeDisplay.isDisplayFormula() &&
molecule.getFormulaElements().size() > 0) {
CMLFormula formula = molecule.getFormulaElements().get(0);
FormulaTool formulaTool = FormulaTool.getOrCreateTool(formula);
SVGElement f = formulaTool.createGraphicsElement(drawable);
if (f != null) {
Real2 formulaOffsets = calculateFormulaOffsets();
Transform2 transform2 = new Transform2(
new double[] {
1., 0., formulaOffsets.getX(),
0., -1., formulaOffsets.getY(),
0., 0., 1.}
);
f.setTransform(transform2);
appendNonNullChild(g, f);
}
}
}
private Real2 calculateFormulaOffsets() {
Real2 offsets = new Real2(getBoundingRectOffsets());
if (moleculeDisplay.getFormulaXPosition().equals(Position.LEFT)) {
offsets.x += moleculeDisplay.getFormulaDisplay().getFontSize() * 0.5;
}
if (moleculeDisplay.getFormulaYPosition().equals(Position.BOTTOM)) {
offsets.y += moleculeDisplay.getFormulaDisplay().getFontSize() * 0.5;
} else if (moleculeDisplay.getFormulaYPosition().equals(Position.TOP)) {
if (boundingRectSVG != null) {
offsets.y = boundingRectSVG.getY();
offsets.y += boundingRectSVG.getHeight();
offsets.y -= moleculeDisplay.getFormulaDisplay().getFontSize() * 1.5;
}
}
return offsets;
}
private Real2 calculateNameOffsets() {
Real2 offsets = new Real2(getBoundingRectOffsets());
if (moleculeDisplay.getNameXPosition().equals(Position.LEFT)) {
offsets.x += moleculeDisplay.getNameDisplay().getFontSize() * 0.2;
}
if (moleculeDisplay.getNameYPosition().equals(Position.BOTTOM)) {
offsets.y += moleculeDisplay.getNameDisplay().getFontSize() * 0.5;
} else if (moleculeDisplay.getNameYPosition().equals(Position.TOP)) {
offsets.y = boundingRectSVG.getY();
offsets.y += moleculeDisplay.getNameDisplay().getFontSize() * 0.5;
}
return offsets;
}
private Real2 getBoundingRectOffsets() {
Real2 offsets = new Real2(0.0, 0.0);
if (boundingRectSVG != null) {
offsets.x = boundingRectSVG.getX();
offsets.y = boundingRectSVG.getY();
}
return offsets;
}
public void enableMoleculeDisplay() {
if (moleculeDisplay == null) {
moleculeDisplay = MoleculeDisplay.getDEFAULT();
}
}
/**
* @return the atomToolMap
*/
public Map getAtomToolMap() {
return atomToolMap;
}
/**
* @return the bondToolMap
*/
public Map getBondToolMap() {
return bondToolMap;
}
/**
* @return the selectionTool
*/
public SelectionTool getOrCreateSelectionTool() {
if (selectionTool == null) {
selectionTool = new SelectionTool();
}
return selectionTool;
}
/**
* @return the selectionTool
*/
public SelectionTool getSelectionTool() {
return selectionTool;
}
/**
* @param selectionTool the selectionTool to set
*/
public void setSelectionTool(SelectionTool selectionTool) {
this.selectionTool = selectionTool;
}
/**
* @return the MoleculeDisplay
*/
public MoleculeDisplay getMoleculeDisplay() {
if (moleculeDisplay == null) {
moleculeDisplay = new MoleculeDisplay(this);
}
return moleculeDisplay;
}
/**
* @param MoleculeDisplay the MoleculeDisplay to set
*/
public void setMoleculeDisplay(MoleculeDisplay MoleculeDisplay) {
this.moleculeDisplay = MoleculeDisplay;
}
//
// public void setCoordinateAxes(CoordinateType type, AxisType axis) {
// if (CoordinateType.CARTESIAN.equals(type)) {
//// String hand = molecule.get
// } else {
// throw new RuntimeException("May not change 3D axes handedness");
// }
// }
/**
* @return the currentAtom
*/
public CMLAtom getCurrentAtom() {
return currentAtom;
}
/**
* @param currentAtom the currentAtom to set
*/
public void setCurrentAtom(CMLAtom currentAtom) {
this.currentAtom = currentAtom;
}
/**
* @return the currentBond
*/
public CMLBond getCurrentBond() {
return currentBond;
}
/**
* @param currentBond the currentBond to set
*/
public void setCurrentBond(CMLBond currentBond) {
this.currentBond = currentBond;
}
/**
* clears currentBond and then ensureCurrentBond()
* @return currentBond
*/
public CMLBond resetCurrentBond() {
this.currentBond = null;
ensureCurrentBond();
return currentBond;
}
/** makes sure there is a current atom.
* if none set, select first atom (fragile)
* @return current atom or null if no atoms
*/
public CMLAtom ensureCurrentAtom() {
if (currentAtom == null && molecule.getAtomCount() > 0) {
currentAtom = molecule.getAtoms().get(0);
}
return currentAtom;
}
/** makes sure there is a current bond.
* if none set, select first ligandBond on current atom (fragile)
* @return current bond or null if currentAtom is null or has no ligands
*/
public CMLBond ensureCurrentBond() {
if (currentBond == null && currentAtom != null) {
List ligandBonds = currentAtom.getLigandBonds();
if (ligandBonds.size() > 0) {
currentBond = ligandBonds.get(0);
}
}
return currentBond;
}
/** steps through ligands of currentAtom.
* if currentBond contains currentAtom, steps through
* ligands of currentAtom else no-op
* @return next ligand bond or null if no currentBond
*/
public CMLBond incrementCurrentBond() {
ensureCurrentBond();
if (currentBond != null) {
List ligands = currentAtom.getLigandBonds();
int idx = ligands.indexOf(currentBond);
if (idx != -1) {
idx = (idx+1) % ligands.size();
currentBond = ligands.get(idx);
}
}
return currentBond;
}
/**
* normalize all molecules which are descendant of node
* @param node
*/
public static void normalizeDescendantMolecules(Node node) {
Nodes molecules = node.query(".//*[local-name()='molecule']");
for (int i = 0; i < molecules.size(); i++) {
MoleculeTool.getOrCreateTool((CMLMolecule)molecules.get(i)).normalize();
}
}
/** normalize.
* may be obsolete
* currently adjusts Hydrogen counts to valency
* also adds formula representing atomArray contents
*/
public void normalize() {
this.adjustHydrogenCountsToValency(
HydrogenControl.ADD_TO_HYDROGEN_COUNT);
Nodes nodes = molecule.query("./*[local-name()='formula' and @convention='"+Convention.ATOMARRAY+"']");
for (int i = 0; i < nodes.size(); i++) {
nodes.get(i).detach();
}
CMLFormula formula = new CMLFormula(molecule);
formula.setConvention(Convention.ATOMARRAY.v);
molecule.appendChild(formula);
}
/** renumbers atoms in molecule.
* uses renumberAtomId
* includes any attributes including atomRef/atomRefs2/atomRefs3/atomRefs4/atomRefs
* @param fromIdList
* @param toIdList
* @throws RuntimeException if toId is already in molecule or if lists contain duplicates
*/
public void renumberAtomIds(List fromIdList, List toIdList) {
if (fromIdList == null || toIdList == null) {
throw new RuntimeException("lists must not be null");
}
if (fromIdList.size() != toIdList.size()) {
throw new RuntimeException("Lists must be of equal size, "+fromIdList.size()+ " / "+toIdList.size());
}
checkUnique(fromIdList);
checkUnique(toIdList);
for (int i = 0; i < fromIdList.size(); i++) {
renumberAtomId(fromIdList.get(i), toIdList.get(i));
}
generateBondIds();
}
/** force renumbering to a1, a2... a(size)
*
*/
public void renumberAtomIdsSequentially() {
List fromIdList = this.getAtomIds();
List toIdList = MoleculeTool.createSequentialAtomIds("a", fromIdList.size());
renumberAtomIds(fromIdList, toIdList);
}
/** returns a list of ids idRoot+1, idRoot+2 ... idRoot+size
*
* @param idRoot (e.g. "a"
* @param size
* @return list (e.g.) a1, a2 ...
*/
public static List createSequentialAtomIds(String idRoot, int size) {
List atomIds = new ArrayList(size);
for (int i = 1; i <= size; i++) {
atomIds.add(idRoot+i);
}
return atomIds;
}
/** return atom ids in order
*
*/
public List getAtomIds() {
List atoms = molecule.getAtoms();
List atomIds = new ArrayList(atoms.size());
for (int i = 0, max = atoms.size(); i < max; i++) {
atomIds.add(atoms.get(i).getId());
}
return atomIds;
}
/**
* for each bond apply Bond.createId
*/
public void createBondIdsFromAtomRefs2() {
List bonds = molecule.getBonds();
for (CMLBond bond : bonds) {
String id = bond.createId();
bond.setId(id);
}
}
private Set checkUnique(List stringList) {
Set stringSet = new HashSet();
for (String string : stringList) {
if (stringSet.contains(string)) {
throw new RuntimeException("duplicate id: "+string);
}
stringSet.add(string);
}
return stringSet;
}
/** renumbers atom in molecule.
* includes any attributes including atomRef/atomRefs2/atomRefs3/atomRefs4/atomRefs
* @param fromId
* @param toId
* @throws RuntimeException if toId is already in molecule
*/
public void renumberAtomId(String fromId, String toId) {
if (molecule.getAtomById(toId) != null) {
throw new RuntimeException("Atom with id already in molecule: "+toId);
}
CMLAtom atom = molecule.getAtomById(fromId);
if (atom != null) {
Nodes nodes = molecule.query(
".//*[@atomRef or @atomRefs2 or @atomRefs3 or @atomRefs4 or @atomRefs]", CMLConstants.CML_XPATH);
for (int i = 0; i < nodes.size(); i++) {
renumberAtomRefs((CMLElement)nodes.get(i), fromId, toId);
}
atom.resetId(toId);
}
}
private void renumberAtomRefs(CMLElement element, String fromId, String toId) {
for (int i = 0; i < element.getAttributeCount(); i++) {
Attribute attribute = element.getAttribute(i);
if (attribute.getLocalName().indexOf("atomRef") != -1) {
String[] values = attribute.getValue().split(S_WHITEREGEX);
for (int j = 0; j < values.length; j++) {
if (values[j].equals(fromId)) {
values[j] = toId;
}
}
attribute.setValue(Util.concatenate(values, CMLConstants.S_SPACE));
element.addAttribute(attribute);
}
}
}
/** get single electron by id
* @param id
* @return electron
*/
public CMLElectron getElectronById(String id) {
Nodes electronNodes = molecule.query(".//cml:electron[@id='"+id+"']", CMLConstants.CML_XPATH);
if (electronNodes.size() > 1) {
throw new RuntimeException("Electrons with duplicate id:"+id);
}
return (electronNodes.size() == 0) ? null : (CMLElectron) electronNodes.get(0);
}
/** gets molar volume.
* if molecule has a child property, uses that.
* else if molecule has a child property for density uses that
* else fails
* new property has units of CMLCUBED
* @return property with molarVolume
*/
public CMLProperty getMolarVolume() {
CMLProperty volume = CMLProperty.getProperty(molecule, CMLProperty.Prop.MOLAR_VOLUME.value);
if (volume == null) {
CMLProperty density = CMLProperty.getProperty(molecule, CMLProperty.Prop.DENSITY.value);
if (density == null) {
throw new RuntimeException("Cannot calculate molar volume without density");
}
if (!Units.GRAM_PER_CMCUBED.value.equals(density.getUnits())) {
throw new RuntimeException("Cannot use density without units=g.cm-3");
}
double densityV = density.getDouble();
double volumeV = this.getCalculatedMolecularMass() / densityV;
volume = new CMLProperty(CMLProperty.Prop.MOLAR_VOLUME.value,
volumeV, Units.CMCUBED.value);
}
return volume;
}
/** gets molar mass.
* if molecule has a child property, uses that.
* else if molecule has atomArray uses that to count atoms
* else if molecule has a child formula uses that
* else fails
* new property has units of Units.GRAM_PER_MOLE
* @return property with molarMass
*/
public CMLProperty getMolarMass() {
CMLProperty mass = CMLProperty.getProperty(molecule, CMLProperty.Prop.MOLAR_MASS.value);
if (mass == null) {
double massV = this.getCalculatedMolecularMass();
if (!Double.isNaN(massV)) {
mass = new CMLProperty(CMLProperty.Prop.MOLAR_MASS.value,
massV, Units.GRAM_PER_MOLE.value);
}
}
return mass;
}
/** fits two aligned molecules.
* normally connection tables will be identical
* ignores any atoms for which there are no 3D coordinates
* also ignores any multiple atom sets in map
* @param map atom-atom map
* @param molecule
* * @return
*/
MoleculePair fitToMoleculeTool(CMLMap map, CMLMolecule moleculeRef) {
MoleculeTool moleculeRefTool = MoleculeTool.getOrCreateTool(moleculeRef);
List atomListi = this.getAtomList(map, Direction.FROM);
Point3Vector p3vi = AtomTool.getPoint3Vector(atomListi);
List atomListj = moleculeRefTool.getAtomList(map, Direction.TO);
Point3Vector p3vj = new Point3Vector(AtomTool.getPoint3Vector(atomListj));
Point3Vector.removeNullValues(p3vi, p3vj);
p3vj = new Point3Vector(p3vj);
Transform3 t3 = null;
if (p3vi.size() > 3) {
t3 = p3vj.fitTo(p3vi);
Point3Vector pvtemp = new Point3Vector(p3vj);
pvtemp.transform(t3);
LOG.debug("RMS "+pvtemp.rms(p3vi)+" / \n");
}
List fromSets = map.getFromSetRefs();
// for (String[] from : fromSets) {
// for (String s : from) {
// Util.prints+" ");
// }
// LOG.debug("...");
// }
List toSets = map.getToSetRefs();
// for (String[] to : toSets) {
// for (String s : to) {
// Util.prints+" ");
// }
// LOG.debug("...");
// }
AtomMatcher atomMatcher = new AtomMatcher3D();
for (int i = 0; i < fromSets.size(); i++) {
CMLAtomSet atomSet1 = new CMLAtomSet(molecule, fromSets.get(i));
CMLAtomSet atomSet2 = new CMLAtomSet(moleculeRef, toSets.get(i));
CMLMap geomMap = ((AtomMatcher3D)atomMatcher).match(atomSet1, atomSet2, t3);
geomMap.debug("GEOMMAP");
}
MoleculePair moleculePair = new MoleculePair(this.molecule, moleculeRef);
moleculePair.setTransform3(t3);
moleculePair.setMap(map);
return moleculePair;
}
/**
* joins RGroups to molecule.
* Looks for atoms with elementType="R" and a child label
* cml:atom[@elementYype='R' and cml:label]
* takes value of label ( labelS = cml:label/@value)
* Looks for correspoding defintion of R as a descendant cml:molecule within
* scopeElement,
* i.e. scopeElement.query(".//cml:atom[label/@value=$labelS", CMLConstants.CML_XPATH)
* @deprecated doen't work at all
*/
public void joinRGroupsExplicitly(Element scopeElement) {
List atomList = molecule.getAtoms();
for (CMLAtom atom : atomList) {
if ("R".equals(atom.getElementType())) {
// AtomTool atomTool = AtomTool.getOrCreateTool(atom);
//FIXME
// CMLAtom refAtom = atomTool.getReferencedGroup(scopeElement);
}
}
}
/** removes all child nodes except atomArray and bondArray and molecule.
* This is because many public programs only accept these nodes
*/
public void stripNonArray() {
Nodes nodes = molecule.query(
"*[not(self::cml:atomArray) and " +
"not(self::cml:bondArray) and "+
"not(self::cml:molecule)]",
CML_XPATH);
for (int i = 0; i < nodes.size(); i++) {
nodes.get(i).detach();
}
}
/**
* calculates Morgan (caches result)
* @return cached Morgan
*/
public Morgan calculateMorgan() {
if (morgan == null) {
morgan = new Morgan(molecule);
}
return morgan;
}
/**
* calculate list of atom equivalence classes
* @return list of equivalent atoms as atomSets
*/
public List calculateListOfMorganAtomMatches() {
calculateMorgan();
List atomSetList = morgan.getAtomSetList();
return atomSetList;
}
/**
*
* @return morgan string
*/
public String calculateMorganString() {
calculateMorgan();
String morganString = morgan.getEquivalenceString();
return morganString;
}
/** sets morgan to null
*
*/
public void clearMorgan() {
this.morgan = null;
}
/** converts a mass to a molarAmount.
* @param molecule
* @return molar Amount (units are mol)
*/
public CMLAmount getMolarAmount(CMLAmount amount0) {
CMLAmount molarAmount = null;
UnitsAttribute units = (UnitsAttribute) amount0.getUnitsAttribute();
if (units == null) {
throw new RuntimeException("No units given on amount");
}
String unitValue = (String) units.getCMLValue();
if (Units.GRAM.value.equals(unitValue)) {
double d = this.getCalculatedMolecularMass(HydrogenControl.NO_EXPLICIT_HYDROGENS);
double amountx = amount0.getXMLContent();
molarAmount = new CMLAmount();
molarAmount.setUnits(Units.MMOL.value);
molarAmount.setXMLContent(amountx / d);
} else {
throw new RuntimeException("Cannot handle units other than gram");
}
return molarAmount;
}
/** converts a volume to a molarAmount.
* @param molecule (must contain data for molarVolume - moleculeTool.getMolarVolume())
* @return molar Amount (units are mol)
*/
public CMLAmount getMolarAmountFromVolume(CMLAmount amount0) {
CMLAmount molarAmount = null;
UnitsAttribute units = (UnitsAttribute) amount0.getUnitsAttribute();
if (units == null) {
throw new RuntimeException("No units given on amount");
}
String unitValue = (String) units.getCMLValue();
if (unitValue != null && Units.ML.value.equals(unitValue)) {
CMLProperty volume = MoleculeTool.getOrCreateTool(molecule).getMolarVolume();
if (volume != null) {
double amountv = amount0.getXMLContent();
molarAmount = new CMLAmount();
molarAmount.setUnits(Units.MOL.value);
molarAmount.setXMLContent(amountv / volume.getDouble());
}
}
return molarAmount;
}
/**
* Calculate formula.
*
* @param control
* USE_EXPLICIT_HYDROGENS (do not use hydrogenCount) OR
* USE_HYDROGEN_COUNT (use hydrogenCount and ignore explicit H)
* @throws RuntimeException
* @return formula
*/
public CMLFormula calculateFormula(HydrogenControl control)
throws RuntimeException {
CMLFormula form = new CMLFormula();
if (!control.equals(HydrogenControl.USE_HYDROGEN_COUNT)
&& !control.equals(HydrogenControl.USE_EXPLICIT_HYDROGENS)) {
throw new RuntimeException(
"No hydrogen count control on Formula - found(" + control
+ CMLConstants.S_RBRAK);
}
CMLAtomSet atomSet = CMLAtomSet.createFromAtoms(molecule.getAtoms());
form = atomSet.getCalculatedFormula(control);
return form;
}
/**
* creates and adds cartesian coordinates from crystal and fractional
* coordinates.
*
* @param crystal
*/
public void createCartesiansFromFractionals(CMLCrystal crystal) {
this.createCartesiansFromFractionals(crystal
.getOrthogonalizationMatrix());
}
/**
* creates and adds cartesian coordinates from orthogonalizationMatrix and
* fractional coordinates.
*
* @param orthogonalMatrix
*/
public void createCartesiansFromFractionals(
Transform3 orthogonalMatrix) {
for (CMLAtom atom : molecule.getAtoms()) {
Point3 point = atom.getPoint3(CoordinateType.FRACTIONAL);
if (point != null) {
point = point.transform(orthogonalMatrix);
atom.setPoint3(point, CoordinateType.CARTESIAN);
}
}
}
/**
* creates and adds cartesian coordinates from orthogonalizationMatrix and
* fractional coordinates.
*
* @param orthogonalMatrix
* @throws RuntimeException
*/
void createCartesiansFromFractionals(
RealSquareMatrix orthogonalMatrix) {
Transform3 t = null;
if (orthogonalMatrix == null || orthogonalMatrix.getCols() != 3) {
throw new RuntimeException("invalid or null orthogonalMatrix");
}
try {
t = new Transform3(orthogonalMatrix);
} catch (EuclidRuntimeException e) {
throw new RuntimeException("invalid orthogonalMatrix");
}
this.createCartesiansFromFractionals(t);
}
/**
* finds angle in XOM corresponding to 3 atoms.
*
* @param at0
* first atom
* @param at1
* middle atom
* @param at2
* third atom
* @return CMLAngle in XOM
* @throws RuntimeException
*/
public CMLAngle getAngle(CMLAtom at0, CMLAtom at1, CMLAtom at2) throws RuntimeException {
if (at0 == null || at1 == null || at2 == null) {
throw new RuntimeException("FindAngle: null atom(s)");
}
for (CMLAngle angle : this.getAngleElements()) {
String[] atomRefs3 = angle.getAtomRefs3();
if (atomRefs3 == null) {
continue;
}
CMLAtom a1 = molecule.getAtomById(atomRefs3[1]);
if (!at1.equals(a1)) {
continue;
}
CMLAtom a0 = molecule.getAtomById(atomRefs3[0]);
CMLAtom a2 = molecule.getAtomById(atomRefs3[2]);
if ((a0.equals(at0) && a2.equals(at2))
|| (a0.equals(at2) && a2.equals(at0))) {
return angle;
}
}
return null;
}
/**
* calculates formula for molecule or each molecule child.
*
* @param control
* treatment of hydrogens
*/
public void calculateAndAddFormula(HydrogenControl control) {
List molecules = this.getMoleculeElements();
if (molecules.size() > 0) {
for (CMLMolecule molecule1 : molecules) {
MoleculeTool moleculeTool1 = MoleculeTool.getOrCreateTool(molecule1);
moleculeTool1.calculateAndAddFormula(control);
}
} else {
CMLFormula formula = null;
formula = this.calculateFormula(control);
molecule.appendChild(formula);
}
}
/**
* get atomSet for all atoms.
*
* @return the atomSet
*/
public CMLAtomSet getAtomSet() {
return new CMLAtomSet(molecule);
}
/**
* get formula.
* if molecule has atoms uses those, else uses formula else null
* @param control
* @return calculated formula
* @throws RuntimeException
*/
public CMLFormula getCalculatedFormula(CMLMolecule.HydrogenControl control) {
CMLFormula formula = null;
CMLAtomSet atomSet = getAtomSet();
if (atomSet == null || atomSet.size() == 0) {
formula = this.getFormulaElements().get(0);
} else {
formula = atomSet.getCalculatedFormula(control);
}
return formula;
}
/**
* get calculated molecular mass.
* @param control
* @return calculated molecular mass.
* @throws RuntimeException unknown/unsupported element type (Dummy counts as zero mass)
*/
public double getCalculatedMolecularMass(CMLMolecule.HydrogenControl control) throws RuntimeException {
CMLFormula formula = this.getCalculatedFormula(control);
if (formula == null) {
throw new RuntimeException("Cannot calculate formula");
}
return formula.getCalculatedMolecularMass();
}
// /**
// * get calculated molecular mass. Assumes correct hydrogen count
// * @return calculated molecular mass.
// * @throws RuntimeException unknown/unsupported element type (Dummy counts as zero mass)
// * @deprecated use MoleculeTool.getCalculatedMolecularMass()
// */
// public double getCalculatedMolecularMass() throws RuntimeException {
// return this.getCalculatedMolecularMass(HydrogenControl.NO_EXPLICIT_HYDROGENS);
// }
/** calculate 3D centroid.
*
* @param type CARTESIAN or FRACTIONAL
* @return centroid of 3D coords or null
*/
public Point3 calculateCentroid3(CoordinateType type) {
CMLAtomSet atomSet = getAtomSet();
return atomSet.getCentroid3(type);
}
/** calculate 3D range.
*
* @param type CARTESIAN or FRACTIONAL
* @return range of 3D coords or null
*/
public Real3Range calculateRange3(CoordinateType type) {
CMLAtomSet atomSet = getAtomSet();
return atomSet.calculateRange3(type);
}
/**
* get id mapping between molecules of equal size.
*
* default is to assume atoms in same order. map is owned by this document
* but is not appended to molecule.
*
* @param mol2
* to compare
* @exception RuntimeException
* atoms cannot be mapped
* @return the map
*/
public CMLMap getMap(CMLMolecule mol2) {
CMLMap map = new CMLMap();
map.addAttribute(new Attribute("toMolecule", mol2.getId()));
List atoms = molecule.getAtoms();
List atoms2 = mol2.getAtoms();
if (atoms.size() != atoms2.size()) {
throw new RuntimeException(
"MoleculesMolecules have different lengths: "
+ atoms.size() + " != " + atoms2.size());
}
for (int i = 0; i < atoms.size(); i++) {
String id = ((CMLAtom) atoms.get(i)).getId();
String id2 = ((CMLAtom) atoms2.get(i)).getId();
String e1 = ((CMLAtom) atoms.get(i)).getElementType();
String e2 = ((CMLAtom) atoms2.get(i)).getElementType();
if (!(e1 == null && e2 == null ||
e1.equals(e2))) {
throw new RuntimeException(
"atoms have different excludeElementTypes: " + id + CMLConstants.S_LBRAK
+ e1 + ") != " + id2 + CMLConstants.S_LBRAK + e2 + CMLConstants.S_RBRAK);
}
CMLLink link = new CMLLink();
link.setFrom(id);
link.setTo(id2);
map.addLink(link);
}
return map;
}
/** get matched atom. does not work with sets
*
* if atom id="a1" and map hass a link to="a1" from="b1" and toFrom =
* Direction.FROM then will return atom id="b1" in resultMolecule
*
* @param atom0
* atom to search with. Its id must occur in a single toFrom
* attribute
* @param map
* with links
* @param toFrom
* specifies attribute for target atom
* @return mapped atom or null
*/
public CMLAtom getMappedAtom(CMLMap map, CMLAtom atom0, Direction toFrom) {
CMLAtom targetAtom = null;
if (atom0 != null && map != null) {
String targetId = map.getRef(atom0.getId(), toFrom);
if (targetId != null) {
targetAtom = molecule.getAtomById(targetId);
}
}
return targetAtom;
}
/**
* gets vector of 3D coordinates.
*
* all atoms must have coordinates
*
* @param type
* CARTESIAN or FRACTIONAL
* @return the vector (null if missing 3D coordinates)
*/
public Point3Vector getCoordinates3(CoordinateType type) {
CMLAtomSet atomSet = this.getAtomSet();
return atomSet.getCoordinates3(type);
}
/**
* generate list of transformed molecules. operates on Cartesians; does not
* modify this
*
* @param sym
* the symmetries
* @return list of molecules
*/
public List transformCartesians(CMLSymmetry sym) {
List molList = new ArrayList();
for (CMLTransform3 tr : sym.getTransform3Elements()) {
CMLMolecule molecule = new CMLMolecule(this.getMolecule());
MoleculeTool moleculeTool = MoleculeTool.getOrCreateTool(molecule);
moleculeTool.transformCartesians(tr);
}
return molList;
}
/**
* transform 3D Cartesian coordinates. modifies this
*
* @param transform
* the transformation
*/
public void transformCartesians(CMLTransform3 transform) {
CMLAtomSet atomSet = this.getAtomSet();
if (atomSet != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
atomSetTool.transformCartesians(transform);
}
}
/** transform 3D Cartesian coordinates. modifies this
*
* @param transform
* the transformation
*/
public void transformCartesians(Transform3 transform) {
CMLAtomSet atomSet = this.getAtomSet();
if (atomSet != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
atomSetTool.transformCartesians(transform);
}
}
/** generate list of transformed molecules. operates on fractionals; does
* modify Cartesians or modify this
*
* @param sym
* the symmetries
* @return list of molecules
*/
public List transformFractionalCoordinates(CMLSymmetry sym) {
List molList = new ArrayList();
for (CMLTransform3 tr : sym.getTransform3Elements()) {
CMLMolecule molecule = new CMLMolecule(this.getMolecule());
MoleculeTool moleculeTool = MoleculeTool.getOrCreateTool(molecule);
moleculeTool.transformFractionalCoordinates(tr);
molList.add(molecule);
}
return molList;
}
/**
* transform 3D fractional coordinates. modifies this does not affect x3,
* y3, z3 (may need to re-generate cartesians)
*
* @param transform
* the transformation
*/
public void transformFractionalCoordinates(CMLTransform3 transform) {
CMLAtomSet atomSet = this.getAtomSet();
if (atomSet != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
atomSetTool.transformFractionals(transform);
}
}
/**
* transform 3D fractional coordinates. modifies this does not affect x3,
* y3, z3 (may need to re-generate cartesians)
*
* @param transform
* the transformation
*/
public void transformFractionalCoordinates(Transform3 transform) {
CMLAtomSet atomSet = this.getAtomSet();
if (atomSet != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
atomSetTool.transformFractionals(transform);
}
}
/**
* transform fractional and 3D coordinates. does NOT alter 2D coordinates
* transforms fractionals then applies orthogonalisation to result
* @param transform
* the fractional symmetry transformation
* @param orthMat
* orthogonalisation matrix
*/
public void transformFractionalsAndCartesians(CMLTransform3 transform, Transform3 orthMat) {
CMLAtomSet atomSet = this.getAtomSet();
if (atomSet != null) {
AtomSetTool atomSetTool = AtomSetTool.getOrCreateTool(atomSet);
atomSetTool.transformFractionalsAndCartesians(transform, orthMat);
}
}
/**
* transform molecule in 2D.
*
* @param matrix 3*3 matrix apply to all 2D coordinates
*/
public void transform(CMLMatrix matrix) {
if (matrix == null ||
matrix.getRows() != 3 ||
matrix.getColumns() != 3) {
throw new RuntimeException("bad transformation matrix");
}
double[] mm = matrix.getDoubleArray();
Transform2 t2 = new Transform2(mm);
this.transform(t2);
}
/**
* transform
* @param t2
*/
public void transform(Transform2 t2) {
for (CMLAtom atom : molecule.getAtoms()) {
if (atom.hasCoordinates(CoordinateType.TWOD)) {
Real2 dd = new Real2(atom.getX2(), atom.getY2());
dd.transformBy(t2);
atom.setXY2(dd);
}
}
}
/**
* translate molecule in 3D.
*
* @param delta3
* add to all 3D coordinates
*/
public void translate3D(Vector3 delta3) {
CMLAtomSet atomSet = getAtomSet();
if (atomSet != null) {
atomSet.translate3D(delta3);
}
}
/** set this.molecule for all descendant atomSets.
* this is messy but a consequence of the serialization
* of atomSets in CML since molecules are not explicit.
*/
public void setThisInDescendantAtomSets() {
Nodes nodes = molecule.query(".//cml:atomSet", CMLConstants.CML_XPATH);
for (int i = 0; i < nodes.size(); i++) {
CMLAtomSet atomSet = (CMLAtomSet) nodes.get(i);
atomSet.setMolecule(molecule);
}
}
/** moves centroid to origin.
*
*/
public void translateCentroidToOrigin3(CoordinateType type) {
AtomSetTool.getOrCreateTool(this.getAtomSet()).translateCentroidToOrigin3(type);
}
// /** ensure integrity between list and children.
// * @return CMLMoleculeList.class
// */
// public Class> getIndexableListClass() {
// return CMLMoleculeList.class;
// }
/** create molecule from atomSet.
* clones atoms
* does not add bonds.
* @param atomSet
*/
public static CMLMolecule createMolecule(CMLAtomSet atomSet) {
// this();
CMLMolecule molecule = new CMLMolecule();
List atoms = atomSet.getAtoms();
for (CMLAtom atom : atoms) {
molecule.addAtom(new CMLAtom(atom));
}
return molecule;
}
/** create molecule from atomSet and bondSet.
* clones atoms and bonds
* @param atomSet
* @param bondSet
*/
public static CMLMolecule createMolecule(CMLAtomSet atomSet, CMLBondSet bondSet) {
CMLMolecule molecule = MoleculeTool.createMolecule(atomSet);
List bonds = bondSet.getBonds();
for (CMLBond bond : bonds) {
molecule.addBond(new CMLBond(bond));
}
return molecule;
}
// /** converts a mass to a molarAmount.
// * @param molecule
// * @return molar Amount (units are mol)
// */
// public CMLAmount getMolarAmount(CMLMolecule molecule) {
// CMLAmount molarAmount = null;
// UnitsAttribute units = (UnitsAttribute) molecule.getUnitsAttribute();
// if (units == null) {
// throw new RuntimeException("No units given on amount");
// }
// String unitValue = (String) units.getCMLValue();
// if (Units.GRAM.value.equals(unitValue)) {
// double d = this.getCalculatedMolecularMass(HydrogenControl.NO_EXPLICIT_HYDROGENS);
// double amountx = molecule.getXMLContent();
// molarAmount = new CMLAmount();
// molarAmount.setUnits(Units.MMOL.value);
// molarAmount.setXMLContent(amountx / d);
// } else {
// throw new RuntimeException("Cannot handle units other than gram");
// }
// return molarAmount;
// }
/** get list of length children.
*
* @return list (empty if none)
*/
public List getLengthElements() {
Nodes nodes = molecule.query("./cml:length", CMLConstants.CML_XPATH);
List list = new ArrayList();
for (int i = 0; i < nodes.size(); i++) {
list.add((CMLLength)nodes.get(i));
}
return list;
}
/** get list of angle children.
*
* @return list (empty if none)
*/
public List getAngleElements() {
Nodes nodes = molecule.query("./cml:angle", CMLConstants.CML_XPATH);
List list = new ArrayList();
for (int i = 0; i < nodes.size(); i++) {
list.add((CMLAngle)nodes.get(i));
}
return list;
}
/** get list of torsion children.
*
* @return list (empty if none)
*/
public List getTorsionElements() {
Nodes nodes = molecule.query("./cml:torsion", CMLConstants.CML_XPATH);
List list = new ArrayList();
for (int i = 0; i < nodes.size(); i++) {
list.add((CMLTorsion)nodes.get(i));
}
return list;
}
/** get list of formula children.
*
* @return list (empty if none)
*/
public List getFormulaElements() {
Nodes nodes = molecule.query("./cml:formula", CMLConstants.CML_XPATH);
List list = new ArrayList();
for (int i = 0; i < nodes.size(); i++) {
list.add((CMLFormula)nodes.get(i));
}
return list;
}
/** get list of molecule children.
*
* @return list (empty if none)
*/
public List getMoleculeElements() {
Nodes nodes = molecule.query("./cml:molecule", CMLConstants.CML_XPATH);
List list = new ArrayList();
for (int i = 0; i < nodes.size(); i++) {
list.add((CMLMolecule)nodes.get(i));
}
return list;
}
public static CMLAtomSet createAtomSet(List molecules) {
CMLAtomSet atomSet = null;
if (molecules.size() > 0) {
CMLMolecule newMolecule = new CMLMolecule(molecules.get(0));
MoleculeTool newMoleculeTool = MoleculeTool.getOrCreateTool(newMolecule);
newMoleculeTool.createLabelsOnMolFromLastIds("m1", molecules.get(0));
for (int i = 1, max = molecules.size(); i < max; i++) {
CMLMolecule mol = new CMLMolecule(molecules.get(i));
newMoleculeTool.addMoleculeTo(mol);
newMoleculeTool.createLabelsOnMolFromLastIds("m"+(i+1), molecules.get(i));
}
atomSet = new CMLAtomSet(newMolecule);
}
return atomSet;
}
private void createLabelsOnMolFromLastIds(String prefix, CMLMolecule mol) {
List atoms = mol.getAtoms();
int molCount = mol.getAtomCount();
List thisAtoms = molecule.getAtoms();
int start = thisAtoms.size() - molCount;
for (int i = 0, max = molCount; i < max; i++) {
String id = atoms.get(i).getId();
String labelValue = prefix+"_"+id;
CMLLabel label = new CMLLabel();
label.setCMLValue(labelValue);
thisAtoms.get(i+start).addLabel(label);
}
}
public SVGGBox draw(MoleculeDisplay moleculeDisplay) {
ensure2DCoordinates(moleculeDisplay.isOmitHydrogens());
MoleculeDisplayList displayList = new MoleculeDisplayList();
try {
displayList.setAndProcess(this);
displayList.createOrDisplayGraphics();
} catch (IOException e) {
e.printStackTrace();
}
SVGG g = displayList.getSvg();
if (g == null) {
throw new RuntimeException("NULL GGG");
}
g = replaceThisBySingleGChild(g);
return SVGGBox.createSVGGBox(g);
}
private SVGG replaceThisBySingleGChild(SVGG g) {
if (g != null) {
Nodes gNodes = g.query("./*[local-name()='"+SVGG.TAG+"']");
if (gNodes.size() == 1) {
g = new SVGG((SVGG)gNodes.get(0));
}
}
return g;
}
public void ensure2DCoordinates(boolean omitHydrogen) {
if (!molecule.hasCoordinates(CoordinateType.TWOD, omitHydrogen)) {
MoleculeLayout moleculeLayout = new MoleculeLayout(this);
try {
moleculeLayout.create2DCoordinates(molecule);
} catch (RuntimeException e) {
e.printStackTrace();
LOG.error("Cannot create coordinates: "+e);
}
}
}
public void stripHydrogens() {
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
if (ChemicalElement.AS.H.toString().equals(atom.getElementType())) {
molecule.deleteAtom(atom);
}
}
}
public SVGGBox drawAndTranslateToRectCorner(MoleculeDisplay moleculeDisplay) {
SVGGBox svgg = this.draw(moleculeDisplay);
BoundingRect brect = BoundingRect.createBoundingRect(svgg);
if (brect != null) {
brect.translateGToRectCorner();
}
return svgg;
}
public void addExplicitHydrogensFromHydrogenCount() {
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
if (!ChemicalElement.AS.H.toString().equals(atom.getElementType())) {
Attribute hcatt = atom.getHydrogenCountAttribute();
if (hcatt != null) {
int hc = atom.getHydrogenCount();
for (int i = 0; i < hc; i++) {
CMLAtom hatom = new CMLAtom(atom.getId()+"_h"+(i+1), ChemicalElement.AS.H);
molecule.addAtom(hatom);
CMLBond bond = new CMLBond(atom, hatom);
molecule.addBond(bond);
bond.setOrder(CMLBond.SINGLE_S);
}
hcatt.detach();
}
}
}
}
public static void ensureAtoms(List molecules) {
for (CMLMolecule molecule : molecules) {
if (molecule != null) {
MoleculeTool.getOrCreateTool(molecule).ensureAtomsUsingSMILES();
molecule.normalizeFormulas();
CMLFormula formula = molecule.getFirstConciseFormula();
}
}
}
private void ensureAtomsUsingSMILES() {
CMLAtomArray atomArray = molecule.getAtomArray();
if (atomArray == null) {
String smilesString = getSmilesFromkFormula();
CMLMolecule smilesMolecule = SMILESTool.createMolecule(smilesString);
if (smilesMolecule != null) {
CMLAtomArray smilesAtomArray = smilesMolecule.getAtomArray();
CMLBondArray smilesBondArray = smilesMolecule.getBondArray();
if (smilesAtomArray != null) {
smilesAtomArray.detach();
molecule.addAtomArray(smilesAtomArray);
if (smilesBondArray != null) {
smilesBondArray.detach();
molecule.addBondArray(smilesBondArray);
}
}
}
}
}
public String getSmilesFromkFormula() {
Nodes formulaNodes = molecule.query("./*[local-name()='"+CMLFormula.TAG+"' and" +
" @inline and @convention='SMILES']");
CMLFormula formula = (formulaNodes.size() == 1) ? (CMLFormula) formulaNodes.get(0) : null;
return (formula == null) ? null : formula.getInline();
}
public static Angle getCalculatedAngle2D(CMLAtom atom0, CMLAtom atom1, CMLAtom atom2) {
Angle angle = null;
if (atom0 != null && atom1 != null && atom2 != null) {
Real2 xy0 = atom0.getXY2();
Real2 xy1 = atom1.getXY2();
Real2 xy2 = atom2.getXY2();
try {
angle = Real2.getAngle(xy0, xy1, xy2);
} catch (Exception e) {
// null coordinates
}
}
return angle;
}
public void transformToInertialAxes() {
Point3Vector p3v = new CMLAtomSet(molecule).getCoordinates3(CoordinateType.CARTESIAN);
RealSquareMatrix eigvec = p3v.calculateRotationToInertialAxes();
if (eigvec != null) {
p3v.transform(new Transform3(eigvec));
this.setCoordinates(CoordinateType.CARTESIAN, p3v);
}
}
public void setCoordinates(CoordinateType type, Point3Vector p3v) {
if (molecule.getAtomCount() != p3v.size()) {
throw new RuntimeException("inconsistent numbers of atoms");
}
List atoms = molecule.getAtoms();
for (int i = 0; i < p3v.size(); i++) {
if (CoordinateType.CARTESIAN.equals(type)) {
atoms.get(i).setXYZ3(p3v.get(i));
} else if (CoordinateType.FRACTIONAL.equals(type)) {
atoms.get(i).setXYZFract(p3v.get(i));
}
}
}
/**
* Calculates the precise masses of the different isotopomers of the molecule,
* returning a maximum of ten isotopomers masses
* @param maxCount the maximum number of return values
* @return a list of double pairs (mass, abundance) sorted by abundance. The
* abundance is expressed as a fraction NOT a percentage
*/
public List getCalculatedIsotopomerMasses() {
return getCalculatedIsotopomerMasses(10);
}
/**
* Calculates the precise masses of the different isotopomers of the molecule
* @param maxCount the maximum number of return values
* @return a list of double pairs (mass, abundance) sorted by abundance. The
* abundance is expressed as a fraction NOT a percentage
*/
public List getCalculatedIsotopomerMasses(int maxCount) {
List isotopomers = new ArrayList();
isotopomers.add(new double[] {0, 1d});
for (CMLAtom atom : molecule.getAtoms()) {
isotopomers = addAtomToCurrentIsotopomers(atom, isotopomers);
Collections.sort(isotopomers, new Comparator() {
public int compare(double[] arg0, double[] arg1) {
if (arg0[1] < arg1 [1]) {
return 1;
}
if (arg0[1] > arg1 [1]) {
return -1;
}
return 0;
}
});
isotopomers = mergeIdenticalMasses(isotopomers);
trimIsotopomerList(isotopomers, maxCount);
}
return isotopomers;
}
private void trimIsotopomerList(List isotopomers, int maxCount) {
while (isotopomers.size() > maxCount) {
isotopomers.remove(maxCount);
}
}
private List mergeIdenticalMasses(List isotopomers) {
List newIsotopomers = new ArrayList();
newIsotopomers.add(isotopomers.get(0));
for (int i = 1; i < isotopomers.size(); i++) {
double mass = isotopomers.get(i)[0];
if (Math.abs(mass - newIsotopomers.get(newIsotopomers.size()-1)[0]) < 1E-12 ) {
newIsotopomers.get(newIsotopomers.size()-1)[1] += isotopomers.get(i)[1];
}
else {
newIsotopomers.add(isotopomers.get(i));
}
}
return newIsotopomers;
}
private List addAtomToCurrentIsotopomers(CMLAtom atom, List isotopomers) {
List newIsotopomers = new ArrayList();
for (double[] isotopomer : isotopomers) {
ChemicalElement element = ChemicalElement.getChemicalElement(atom.getElementType());
double [] abundancies = element.getIsotopeAbundances();
for (int i = 0; i < abundancies.length; i++) {
if (element.getIsotopePreciseMasses() == null) {
throw new RuntimeException("isotopes not known for " + element.getSymbol());
}
double newMass = isotopomer[0] + element.getIsotopePreciseMasses()[i];
double newAbundance = isotopomer[1] * abundancies[i]/100;
newIsotopomers.add(new double [] {newMass, newAbundance});
}
}
return newIsotopomers;
}
public void addLoneElectronCountToAtoms() {
List atoms = molecule.getAtoms();
for (CMLAtom atom : atoms) {
int loneElectronCount = this.getLoneElectronCount(atom);
atom.setCMLXAttribute(ElectronTool.LONE_ELECTRONS, ""+loneElectronCount);
}
}
public void addElectronCountToBonds() {
List bonds = molecule.getBonds();
for (CMLBond bond : bonds) {
BondTool bondTool = BondTool.getOrCreateTool(bond);
int electronCount = bondTool.getElectronCount();
bond.setCMLXAttribute(ElectronTool.ELECTRONS, ""+electronCount);
}
}
}
class BoundingRect {
private SVGRect rect;
private double xorig;
private double yorig;
private double width;
private double height;
private SVGG svgg;
private BoundingRect(Nodes rects, SVGG svgg) {
Element rectx = (Element)rects.get(0);
rect = (SVGRect) SVGElement.createSVG(rectx);
xorig = rect.getX();
yorig = rect.getY();
width = rect.getWidth();
height = rect.getHeight();
this.svgg = svgg;
}
public static BoundingRect createBoundingRect(SVGG svgg) {
Nodes rects = svgg.query("./*[local-name()='"+SVGRect.TAG+"']");
return (rects.size() == 0) ? null : new BoundingRect(rects, svgg);
}
public void translateGToRectCorner() {
Transform2 transform = svgg.getTransform2FromAttribute();
if (transform == null) {
transform = new Transform2();
}
// subtract height since we are inverting box
Vector2 translateVector = new Vector2(-xorig, -yorig - height);
Transform2 translate = new Transform2(translateVector);
transform = transform.concatenate(translate);
svgg.setTransform(transform);
}
public double getHeight() {
return height;
}
public double getWidth() {
return width;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy