|
| 1 | +(* ::Package:: *) |
| 2 | + |
| 3 | +BeginPackage["HuckelTheory`"] |
| 4 | + |
| 5 | +HuckelHamiltonianMatrix::usage = |
| 6 | + "HuckelHamiltonianMatrix[m] gives the pi-electron Hamiltonian matrix for the Molecule[] m." |
| 7 | + |
| 8 | +HuckelMO::usage = |
| 9 | + "HuckelMO[m] returns an association whose keys are the eigenvalues and whose values are the corresponding eigenvectors (molecular orbitals) of Molecule[] m" |
| 10 | + |
| 11 | +HuckelChargeDensityMatrix::usage = |
| 12 | + "HuckelChargeDensityMatrix[m] returns the pi-charge density matrix of Molecule[] m" |
| 13 | + |
| 14 | +HuckelMOPlot::usage = |
| 15 | + "HuckelMOPlot[m] returns an association whose keys are the eigenvalues and whose values are plots of the pi-molecular orbitals on the 2D graph of Molecule[] m" |
| 16 | + |
| 17 | +HuckelBondOrderPlot::usage = |
| 18 | + "HuckelBondOrderPlot[m] returns an image with the nearest-neighbor atom pi-bond orders plotted on the 2D dimensional graph of Molecule[] m" |
| 19 | + |
| 20 | +HuckelTotalElectronsPlot::usage = |
| 21 | + "HuckelTotalElectronsPlot[m] returns an image of Molecule[] m depicting the total number of pi electrons on each atomic site." |
| 22 | + |
| 23 | +HuckelNetChargePlot::usage = |
| 24 | + "HuckelNetChargePlot[m] returns an image of Molecule[] m depicting the net pi-charge on each atomic site." |
| 25 | + |
| 26 | +Begin["`Private`"] |
| 27 | + |
| 28 | +Once[If[$VersionNumber<12.,Print["Mathematica version 12 or greater is required."];Abort[];]] |
| 29 | + |
| 30 | +HuckelHamiltonianMatrix[m_Molecule,t_:-1]:=With[{m2=sortPiAtoms[m]}, |
| 31 | +With[ |
| 32 | +{atomTypes=MoleculeProperty["AtomList"]@m2, |
| 33 | +piElectrons=effectivePiElectrons@m2, |
| 34 | +conjugatedAtomIndices=piSystemIndices@m2, |
| 35 | +piBonds=piBondList@m2}, |
| 36 | +t*(diagonal[atomTypes,conjugatedAtomIndices,piElectrons]+(*diagonal terms*) |
| 37 | +offDiagonal[atomTypes,piElectrons,Length[conjugatedAtomIndices],piBonds]) |
| 38 | +]] |
| 39 | + |
| 40 | +HuckelMO[m_Molecule,t_:-1.]:=Map[Chop,AssociationThread@@Eigensystem@HuckelHamiltonianMatrix[m,t]]//KeySort |
| 41 | + |
| 42 | +HuckelChargeDensityMatrix[m_Molecule,t_:-1.]:=With[ |
| 43 | +{coeffs=Values@HuckelMO[m,t], |
| 44 | +nOcc=effectivePiElectronCount[m]/2, (*todo: add an option for net charge*) |
| 45 | +nPiAtoms=Length@piSystemIndices@m}, |
| 46 | +Table[ |
| 47 | +Sum[2*coeffs[[i,r]]*coeffs[[i,s]],{i,nOcc}], |
| 48 | +{r,nPiAtoms},{s,nPiAtoms}]//Chop] |
| 49 | + |
| 50 | +onSp2Carbon[pattern_,bondType_:"Single"][m_Molecule]:= |
| 51 | +With[ |
| 52 | +{sp2Carbons=AtomList[m, |
| 53 | +MoleculePattern[{Atom["C","OrbitalHybridization"->"SP2"]}], |
| 54 | +"AtomIndex"], |
| 55 | +targetAtoms=Flatten@BondList[m, |
| 56 | +MoleculePattern[{pattern,Atom["C","OrbitalHybridization"->"SP2"]},{Bond[{1,2},bondType]}], |
| 57 | +"AtomIndex"]}, |
| 58 | +Complement[targetAtoms,sp2Carbons]] |
| 59 | + |
| 60 | +nonRingSP2Atom[m_Molecule,symbol_String]:=AtomList[ |
| 61 | +m, |
| 62 | +MoleculePattern[{Atom[symbol,"OrbitalHybridization"->"SP2","RingMemberQ"->False]}], |
| 63 | +"AtomIndex"] |
| 64 | + |
| 65 | +piSystemIndices[m_Molecule]:=With[ |
| 66 | +{sp2Atoms=AtomList[m, |
| 67 | +MoleculePattern[{Atom[_,"OrbitalHybridization"->"SP2"]}], (*any sp2 species*) |
| 68 | +"AtomIndex"], |
| 69 | +halogens=onSp2Carbon["F"|"Br"|"Cl"][m]}, |
| 70 | +Union[sp2Atoms,halogens]] |
| 71 | + |
| 72 | +sortPiAtoms[m_Molecule]:=With[ |
| 73 | +{piAtoms=piSystemIndices[m], |
| 74 | +allAtoms=MoleculeProperty["AtomIndex"]@m}, |
| 75 | +MoleculeModify[m, {"RenumberAtoms",Join[piAtoms,Complement[allAtoms,piAtoms]]}] |
| 76 | +] |
| 77 | + |
| 78 | +effectivePiElectrons[m_Molecule]:=With[ |
| 79 | +{default=MoleculeProperty["PiElectronCount"]@m, |
| 80 | +conjugatedHalogens=onSp2Carbon["F"|"Br"|"Cl"]@m, |
| 81 | +alcoholOrEtherOxygen=onSp2Carbon["O"]@m, |
| 82 | +anilineLikeNitrogen=nonRingSP2Atom[m,"N"] |
| 83 | +(*need to treat other cases?*)}, |
| 84 | +ReplacePart[default,Partition[ |
| 85 | + Join[conjugatedHalogens,alcoholOrEtherOxygen,anilineLikeNitrogen], |
| 86 | + 1]->2]] |
| 87 | + |
| 88 | +effectivePiElectronCount[m_Molecule]:=Total@effectivePiElectrons[m] |
| 89 | + |
| 90 | +piBondList[m_Molecule]:=With[ |
| 91 | +{bonds=BondList[m], |
| 92 | +piAtoms=piSystemIndices[m]}, |
| 93 | +Select[bonds,ContainsOnly[First[#],piAtoms]&]] |
| 94 | + |
| 95 | +haa[Atom["B"],0]=-1.; |
| 96 | +haa[Atom["N"],1]=0.5; |
| 97 | +haa[Atom["N"],2]=1.5; |
| 98 | +haa[Atom["O"],1]=1.0; |
| 99 | +haa[Atom["O"],2]=2.0; |
| 100 | +haa[Atom["C"],1]=0.0; |
| 101 | +haa[Atom["C"],2]=0.0; (*acetylene should be fine also*) |
| 102 | +haa[Atom["F"],2]=3.0; |
| 103 | +haa[Atom["Cl"],2]=2.0; |
| 104 | +haa[Atom["Br"],2]=1.5; |
| 105 | +hab[{Atom["B"],Atom["C"]},_,_]=0.7; |
| 106 | +hab[{Atom["B"],Atom["N"]},_,_]=0.8; |
| 107 | +hab[{Atom["C"],Atom["N"]} ,_,1]=1.0; |
| 108 | +hab[{Atom["C"],Atom["N"]} ,_,2]=0.8; |
| 109 | +hab[{Atom["C"],Atom["C"]},_,_]=1.0; |
| 110 | +hab[{Atom["C"],Atom["O"]},"Double",1]=1.0; (*carbonyl*) |
| 111 | +hab[{Atom["C"],Atom["O"]},_,2]=0.8; |
| 112 | +hab[{Atom["C"],Atom["Cl"]},_,_]=0.4; |
| 113 | +hab[{Atom["Br"],Atom["C"]},_,_]=0.3; |
| 114 | +hab[{Atom["C"],Atom["F"]},_,_]=0.7; |
| 115 | + |
| 116 | +bondType[Bond[pair_List,type_String],atoms_List,piElectrons_List]:=With[ |
| 117 | +{symbols=Sort@atoms[[pair]], |
| 118 | +atom1=atoms[[pair[[1]]]]}, |
| 119 | +pair->hab[symbols,type, |
| 120 | +If[atom1===Atom["C"], (*get the non-carbon number of pi electrons*) |
| 121 | +piElectrons[[pair[[2]]]], |
| 122 | +piElectrons[[pair[[1]] ]] |
| 123 | +] |
| 124 | +] |
| 125 | +] |
| 126 | + |
| 127 | +offDiagonal[atomTypes_,piElectrons_,conjugatedAtomCount_,piBonds_]:=With[ |
| 128 | +{half=SparseArray[ |
| 129 | +bondType[#,atomTypes,piElectrons]&/@piBonds,{conjugatedAtomCount,conjugatedAtomCount} (*force it to be a square matrix of this size; otherwise it breaks with linear molecules*) |
| 130 | +] |
| 131 | +}, |
| 132 | +half+Transpose[half]] |
| 133 | + |
| 134 | +diagonal[atomTypes_,conjugatedAtomIndices_,piElectrons_]:=With[ |
| 135 | +{rules=MapIndexed[{First@#2,First@#2}->haa@@#1&,Transpose[{atomTypes[[conjugatedAtomIndices]],piElectrons[[conjugatedAtomIndices]]}]] |
| 136 | +}, (*warning...this assumes that the conjugated atoms all come first before non-conjugated...in any case this will get screwy with display too*) |
| 137 | +SparseArray[rules]] |
| 138 | + |
| 139 | +huckelAdjacencyMatrix[m_Molecule]:=With[ |
| 140 | +{atomTypes=MoleculeProperty["AtomList"]@m, |
| 141 | +piElectrons=effectivePiElectrons@m, |
| 142 | +conjugatedAtomCount=Length@piSystemIndices@m, |
| 143 | +piBonds=piBondList@m}, |
| 144 | +(*kind of overkill, but easy*) |
| 145 | +Unitize@offDiagonal[atomTypes,piElectrons,conjugatedAtomCount,piBonds]] |
| 146 | + |
| 147 | +HuckelMOPlot[m_Molecule,t_:-1.]:=With[ |
| 148 | +{MOs=N@HuckelMO[m,t], |
| 149 | +nOcc=effectivePiElectronCount[m]/2}, (*assumes all doubly occupied MOs*) |
| 150 | +AssociationThread[ |
| 151 | +Keys[MOs]-> |
| 152 | +MapThread[ |
| 153 | +moPlot[#1][m,#2]&, |
| 154 | +{ConstantArray[occupiedMO,nOcc]~Join~ConstantArray[unoccupiedMO,(Length[MOs]-nOcc)], |
| 155 | +Values@MOs}] |
| 156 | +]] |
| 157 | + |
| 158 | +HuckelTotalElectronsPlot[m_Molecule,t_:-1.]:=With[ |
| 159 | +{nElectronsHuckel=Chop@Diagonal@HuckelChargeDensityMatrix[m,t]}, |
| 160 | +moPlot[netCharge][m,nElectronsHuckel]] |
| 161 | + |
| 162 | +HuckelNetChargePlot[m_Molecule,t_:-1.]:=With[ |
| 163 | +{nElectronsHuckel=Chop@Diagonal@HuckelChargeDensityMatrix[m,t], |
| 164 | +nElectronsInitial=effectivePiElectrons[m]}, |
| 165 | +moPlot[netCharge][m,(*just take the subset of pi orbitals*) |
| 166 | +nElectronsInitial[[1;;Length[nElectronsHuckel]]]-nElectronsHuckel] |
| 167 | +] |
| 168 | + |
| 169 | +HuckelBondOrderPlot[m_Molecule,t_:-1.]:=With[ |
| 170 | +{chargeDensityMatrix=Chop@HuckelChargeDensityMatrix[m,t], |
| 171 | +piAdjacencyMatrix=huckelAdjacencyMatrix[m]}, |
| 172 | +boPlot[bondOrder][m,chargeDensityMatrix*piAdjacencyMatrix]] |
| 173 | + |
| 174 | +moPlot[colorRule_][m_Molecule,coeffs_List]:=With[ |
| 175 | +{m2=MoleculeModify["RemoveHydrogens"]@sortPiAtoms[m]}, |
| 176 | +With[ |
| 177 | +{xyCoords=m2["AtomDiagramCoordinates"][[piSystemIndices[m2]]], |
| 178 | +refArea=(1/Max[ Max[#,10^-2]&/@Abs[coeffs]]) }, (*handle cases where charge is zero?*) |
| 179 | +Show[ |
| 180 | +MoleculePlot[m2,PlotTheme->"HeavyAtom"], |
| 181 | +Graphics@MapThread[ |
| 182 | +colorRule[#1,#2,refArea]&,(*turn off normalization scaling*) |
| 183 | +{coeffs,xyCoords}]] |
| 184 | +]] |
| 185 | + |
| 186 | +boPlot[colorRule_][m_Molecule,coeffs_?MatrixQ]:=With[ |
| 187 | +{m2=MoleculeModify["RemoveHydrogens"]@sortPiAtoms[m]}, |
| 188 | +With[ |
| 189 | +{xyCoords=m2["AtomDiagramCoordinates"][[piSystemIndices[m2]]], |
| 190 | +nAtoms=Length[coeffs]}, |
| 191 | +Show[ |
| 192 | +MoleculePlot[m2,PlotTheme->"HeavyAtom"], |
| 193 | +Graphics@Flatten@Table[ |
| 194 | +colorRule[coeffs[[r,s]],Midpoint[xyCoords[[{r,s}]]]], |
| 195 | +{r,1,nAtoms-1},{s,r+1,nAtoms}] |
| 196 | +] |
| 197 | +]] |
| 198 | + |
| 199 | +(*I thought I would want independent control over display characteristics like radius, but that's over-rated. Could probably be refactored*) |
| 200 | +occupiedMO[coeff_?NumericQ,xy_List,scaleFactor_:1,arbitraryRadius_:0.5]:=Tooltip[{If[coeff<0,Red,Blue],Opacity[0.3],Disk[xy,Abs[coeff*scaleFactor]*arbitraryRadius]}, |
| 201 | +coeff] |
| 202 | + |
| 203 | +unoccupiedMO[coeff_?NumericQ,xy_List,scaleFactor_:1.,arbitraryRadius_:0.5]:=Tooltip[{If[coeff<0,Yellow,Green],Opacity[0.4],Disk[xy,Abs[coeff*scaleFactor]*arbitraryRadius]}, |
| 204 | +coeff] |
| 205 | + |
| 206 | +netCharge[coeff_?NumericQ,xy_List,scaleFactor_:1., arbitraryRadius_:0.5]:=Tooltip[ |
| 207 | +{If[coeff<0,Red,Black],Opacity[0.3],Disk[xy,Abs[coeff*scaleFactor]*arbitraryRadius]}, |
| 208 | +coeff] |
| 209 | + |
| 210 | +bondOrder[coeff_?NumericQ,xy_List,scaleFactor_:1.,arbitraryRadius_:0.5]:= |
| 211 | +Tooltip[ |
| 212 | +{Orange,Opacity[0.5],Disk[xy,Abs[coeff*scaleFactor]*arbitraryRadius]}, |
| 213 | +coeff] |
| 214 | + |
| 215 | +End[ ] |
| 216 | + |
| 217 | +EndPackage[ ] |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | + |
| 222 | + |
| 223 | + |
0 commit comments