func main() { //This is the part that collects all the data from PyMOL, with all the proper error checking. stdin := bufio.NewReader(os.Stdin) options, err := chemjson.DecodeOptions(stdin) if err != nil { fmt.Fprint(os.Stderr, err.Marshal()) log.Fatal(err) } mainName := options.SelNames[0] if len(options.AtomsPerSel) > 1 { for _, v := range options.SelNames[1:] { mainName = mainName + "__" + v //inefficient but there should never be THAT many selections. } } dielectric := options.FloatOptions[0][0] charge := options.IntOptions[0][0] multi := options.IntOptions[0][1] qmprogram := options.StringOptions[0][0] method := options.StringOptions[0][1] calctype := options.StringOptions[0][2] var osidemol *chem.Topology var osidecoords, sidecoords *v3.Matrix var sidelist, sidefrozen []int selindex := 0 total := 0 selections := len(options.AtomsPerSel) if options.BoolOptions[0][0] { //sidechain selections exist sidecoords, osidecoords, osidemol, sidelist, sidefrozen = SideChains(stdin, options) selections-- total += osidemol.Len() selindex++ } fmt.Fprint(os.Stderr, selections) obbmol := make([]*chem.Topology, selections, selections) obbcoords := make([]*v3.Matrix, selections, selections) bbcoords := make([]*v3.Matrix, selections, selections) bblist := make([][]int, selections, selections) bbfrozen := make([][]int, selections, selections) for i := 0; i < selections; i++ { bbcoords[i], obbcoords[i], obbmol[i], bblist[i], bbfrozen[i] = BackBone(stdin, options, selindex) total += obbmol[i].Len() selindex++ fmt.Fprint(os.Stderr, "chetumanga") } //Now we put the juit together bigC := v3.Zeros(total) bigA := chem.NewTopology([]*chem.Atom{}, 0, 0) bigFroz := make([]int, 0, total) setoffset := 0 if options.BoolOptions[0][0] { bigC.SetMatrix(0, 0, osidecoords) setoffset += osidecoords.NVecs() bigA = chem.MergeAtomers(bigA, osidemol) // bigA = osidemol bigFroz = append(bigFroz, sidefrozen...) } for k, v := range obbcoords { bigC.SetMatrix(setoffset, 0, v) bigA = chem.MergeAtomers(bigA, obbmol[k]) tmpfroz := SliceOffset(bbfrozen[k], setoffset) bigFroz = append(bigFroz, tmpfroz...) setoffset += v.NVecs() } bigA.SetCharge(charge) bigA.SetMulti(multi) chem.PDBFileWrite(mainName+"toOPT.pdb", bigC, bigA, nil) ///////////////////////////////////// chem.XYZFileWrite(mainName+"toOPT.xyz", bigC, bigA) ///////////////////////////////////// //Ok, we have now one big matrix and one big atom set, now the optimization calc := new(qm.Calc) if calctype == "Optimization" { calc.Optimize = true } calc.RI = true //some options, including this one, are meaningless for MOPAC calc.CConstraints = bigFroz calc.Dielectric = dielectric calc.SCFTightness = 1 calc.Dispersion = "D3" calc.Method = "TPSS" if method == "Cheap" { calc.BSSE = "gcp" if qmprogram == "ORCA" { calc.Method = "HF-3c" calc.RI = false } else if qmprogram == "MOPAC2012" { calc.Method = "PM6-D3H4 NOMM MOZYME" } else { calc.Basis = "def2-SVP" } } else { calc.Basis = "def2-TZVP" } //We will use the default methods and basis sets of each program. In the case of MOPAC, that is currently PM6-D3H4. var QM qm.Handle switch qmprogram { case "ORCA": orca := qm.NewOrcaHandle() orca.SetnCPU(runtime.NumCPU()) QM = qm.Handle(orca) case "TURBOMOLE": QM = qm.Handle(qm.NewTMHandle()) case "NWCHEM": QM = qm.Handle(qm.NewNWChemHandle()) calc.SCFConvHelp = 1 default: QM = qm.Handle(qm.NewMopacHandle()) } QM.SetName(mainName) QM.BuildInput(bigC, bigA, calc) fmt.Fprint(os.Stderr, options.BoolOptions) if options.BoolOptions[0][2] { return //Dry run } if err2 := QM.Run(true); err != nil { log.Fatal(err2.Error()) } //Now we ran the calculation, we must retrive the geometry and divide the coordinates among the original selections. var newBigC *v3.Matrix info := new(chemjson.Info) //Contains the return info var err2 error if calc.Optimize { newBigC, err2 = QM.OptimizedGeometry(bigA) if err2 != nil { log.Fatal(err2.Error()) } if qmprogram == "NWCHEM" { //NWchem translates/rotates the system before optimizing so we need to superimpose with the original geometry in order for them to match. newBigC, err2 = chem.Super(newBigC, bigC, bigFroz, bigFroz) if err2 != nil { log.Fatal(err2.Error()) } } info.Molecules = len(options.AtomsPerSel) geooffset := 0 if options.BoolOptions[0][0] { tmp := newBigC.View(geooffset, 0, len(sidelist), 3) //This is likely to change when we agree on a change for the gonum API!!!! sidecoords.SetVecs(tmp, sidelist) info.FramesPerMolecule = []int{1} info.AtomsPerMolecule = []int{sidecoords.NVecs()} //I DO NOT understand why the next line is += len(sidelist)-1 instead of len(sidelist), but it works. If a bug appears //take a look at this line, and the equivalent in the for loop that follows. geooffset += (len(sidelist) - 1) } for k, v := range bbcoords { //Take a look here in case of bugs. tmp := newBigC.View(geooffset, 0, len(bblist[k]), 3) //This is likely to change when we agree on a change for the gonum API!!!! v.SetVecs(tmp, bblist[k]) info.FramesPerMolecule = append(info.FramesPerMolecule, 1) info.AtomsPerMolecule = append(info.AtomsPerMolecule, v.NVecs()) geooffset += (len(bblist[k]) - 1) } // for k,v:=range(bbcoords){ // chem.XYZWrite(fmt.Sprintf("opti%d.xyz",k), , newcoords) // } } else { //nothing here, the else part will get deleted after tests } energy, err2 := QM.Energy() if err2 != nil { log.Fatal(err2.Error()) } //Start transfering data back info.Energies = []float64{energy} if err2 := info.Send(os.Stdout); err2 != nil { fmt.Fprint(os.Stderr, err2) log.Fatal(err2) } // fmt.Fprint(os.Stdout,mar) // fmt.Fprint(os.Stdout,"\n") // A loop again to transmit the info. if options.BoolOptions[0][0] { if err := chemjson.SendMolecule(nil, []*v3.Matrix{sidecoords}, nil, nil, os.Stdout); err2 != nil { fmt.Fprint(os.Stderr, err) log.Fatal(err) } } for _, v := range bbcoords { fmt.Fprintln(os.Stderr, "BB transmit!", v.NVecs()) if err := chemjson.SendMolecule(nil, []*v3.Matrix{v}, nil, nil, os.Stdout); err2 != nil { fmt.Fprint(os.Stderr, err) log.Fatal(err) } } }
func main() { //flag parsing //Lots of options, but the defaults are sane enough that you shouldnt need to use more than the filename and qm program. charge := flag.Int("charge", 0, "The charge of the system.") multi := flag.Int("multi", 1, "The multiplicity of the system.") filename := flag.String("file", "file.xyz", "The XYZ file containing the coordinates for the system.") functional := flag.String("func", "BP86", "The density functional used. TPSS and BP86 activate RI when possible.") program := flag.String("program", "nwchem", "The QM program used: qcmine, nwchem, or orca.") basis := flag.String("basis", "def2-SVP", "the basis set to use. Use Karlsruhe basis.") dielectric := flag.Float64("epsilon", -1, "The dielectric constant. -1 indicates no dielectric used.") optimize := flag.Bool("opt", true, "Wether to optimize or run an SP calculation.") fixed := flag.String("fixed", "", "Fixed atoms, counting from zero, separated by spaces.") flag.Parse() //We set the calculation to the values in the flags. Not big deal. mol, err := chem.XYZFileRead(*filename) if err != nil { panic(err.Error()) } mol.SetMulti(*multi) mol.SetCharge(*charge) calc := new(qm.Calc) if strings.Contains("TPSS,BP86,PBE", *functional) { calc.RI = true } if *fixed != "" { calc.CConstraints, err = scu.IndexStringParse(*fixed) if err != nil { panic(err.Error()) } } calc.Memory = 1000 calc.Dielectric = *dielectric calc.Grid = 3 if !strings.HasPrefix(*basis, "def2") { fmt.Println("Told ya to use Karlsruhe basis! RI cannot be activated. Happy now?") calc.RI = false } calc.Basis = *basis calc.Job.Opti = true //for the preeliminar calculation we always optimize, regarless of the user's input, because it's, well, a preoptimization. if calc.Job.Opti { calc.SCFTightness = 1 } calc.Dispersion = "D3" //The preeliminar optimization will have a different name. namep := strings.Replace(*filename, ".xyz", "OPT", -1) name := strings.Replace(*filename, ".xyz", "", -1) pre := qm.NewMopacHandle() pre.SetName(namep) pre.BuildInput(mol.Coords[0], mol, calc) pre.Run(true) ncoords, err := pre.OptimizedGeometry(mol) if err != nil { panic(err.Error()) } calc.Job.Opti = *optimize //here we optimize depending on what the user wants //MOPAC will overwrite the method, as it doesnt support DFT. This is why we set it AFTER the preeliminar calculations calc.Method = *functional var QM qmdef switch *program { default: QM = qmdef(qm.NewNWChemHandle()) case "orca": QM = qmdef(qm.NewOrcaHandle()) // default: // //There is a hicup with ChemShell since I have not implemented a few methods. // //I should have an interface in goChem that does not require Energy() and OptimizedGeometry() // CS:=qm.NewCSHandle() // CS.SetDefaults() // CS.SetName(name) // CS.BuildInput(ncoords,mol,calc) } if QM != nil { QM.SetDefaults() QM.SetName(name) QM.BuildInput(ncoords, mol, calc) } newfilename := strings.Replace(*filename, ".xyz", "_preopt.xyz", 1) chem.XYZFileWrite(newfilename, ncoords, mol) fmt.Fprintln(os.Stderr, "Apparently, we made it :-)") }