github.com/rmera/gochem@v0.7.1/qm/orca.go (about)

     1  /*
     2   * qm.go, part of gochem.
     3   *
     4   *
     5   * Copyright 2012 Raul Mera <rmera{at}chemDOThelsinkiDOTfi>
     6   *
     7   * This program is free software; you can redistribute it and/or modify
     8   * it under the terms of the GNU Lesser General Public License as
     9   * published by the Free Software Foundation; either version 2.1 of the
    10   * License, or (at your option) any later version.
    11   *
    12   * This program is distributed in the hope that it will be useful,
    13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15   * GNU General Public License for more details.
    16   *
    17   * You should have received a copy of the GNU Lesser General
    18   * Public License along with this program.  If not, see
    19   * <http://www.gnu.org/licenses/>.
    20   *
    21   *
    22   */
    23  
    24  package qm
    25  
    26  import (
    27  	"fmt"
    28  	"log"
    29  	"os"
    30  	"os/exec"
    31  	"runtime"
    32  	"strconv"
    33  	"strings"
    34  
    35  	chem "github.com/rmera/gochem"
    36  	v3 "github.com/rmera/gochem/v3"
    37  )
    38  
    39  //OrcaHandle represents an Orca calculation.
    40  //Note that the default methods and basis vary with each program, and even
    41  //for a given program they are NOT considered part of the API, so they can always change.
    42  type OrcaHandle struct {
    43  	defmethod   string
    44  	defbasis    string
    45  	defauxbasis string
    46  	previousMO  string
    47  	bsse        string
    48  	command     string
    49  	inputname   string
    50  	wrkdir      string
    51  	nCPU        int
    52  	orca3       bool
    53  }
    54  
    55  //NewOrcaHandle initializes and returns a new OrcaHandle.
    56  func NewOrcaHandle() *OrcaHandle {
    57  	run := new(OrcaHandle)
    58  	run.SetDefaults()
    59  	return run
    60  }
    61  
    62  //OrcaHandle methods
    63  
    64  //SetnCPU sets the number of CPU to be used.
    65  func (O *OrcaHandle) SetnCPU(cpu int) {
    66  	O.nCPU = cpu
    67  }
    68  
    69  //SetName sets the name of the job, which will reflect in the
    70  //name fo the input and output files.
    71  func (O *OrcaHandle) SetName(name string) {
    72  	O.inputname = name
    73  }
    74  
    75  //SetCommand sets the name and path of the Orca excecutable
    76  func (O *OrcaHandle) SetCommand(name string) {
    77  	O.command = name
    78  }
    79  
    80  //SetMOName sets the name of the file containing molecular
    81  //orbitales (in the corresponding Orca format) to be
    82  //used as initial guess.
    83  func (O *OrcaHandle) SetMOName(name string) {
    84  	O.previousMO = name
    85  }
    86  
    87  //SetWorkDir sets the name of the working directory for the calculation
    88  func (O *OrcaHandle) SetWorkDir(d string) {
    89  	O.wrkdir = d
    90  }
    91  
    92  //SetOrca3 sets the use of Orca3 to true or false. The default state
    93  //is false, meaning that Orca4 is used.
    94  func (O *OrcaHandle) SetOrca3(b bool) {
    95  	O.orca3 = b
    96  }
    97  
    98  //SetDefaults Sets defaults for ORCA calculation. The default is
    99  //currently a single-point at
   100  //revPBE/def2-SVP with RI, and all the available CPU with a max of
   101  //8. The ORCA command is set to $ORCA_PATH/orca, at least in
   102  //unix.
   103  //The default is _not_ part of the API, it can change as new methods appear.
   104  func (O *OrcaHandle) SetDefaults() {
   105  	O.defmethod = "BLYP"
   106  	O.defbasis = "def2-SVP"
   107  	O.defauxbasis = "def2/J"
   108  	if O.orca3 {
   109  		O.defauxbasis = "def2-SVP/J"
   110  	}
   111  	O.command = os.ExpandEnv("${ORCA_PATH}/orca")
   112  	if O.command == "/orca" { //if ORCA_PATH was not defined
   113  		O.command = "./orca"
   114  	}
   115  	cpu := runtime.NumCPU() / 2
   116  	O.nCPU = cpu
   117  
   118  }
   119  
   120  //BuildInput builds an input for ORCA based int the data in atoms, coords and C.
   121  //returns only error.
   122  func (O *OrcaHandle) BuildInput(coords *v3.Matrix, atoms chem.AtomMultiCharger, Q *Calc) error {
   123  	if O.wrkdir != "" {
   124  		O.wrkdir = O.wrkdir + "/"
   125  	}
   126  	//Only error so far
   127  	if atoms == nil || coords == nil {
   128  		return Error{ErrMissingCharges, Orca, O.inputname, "", []string{"BuildInput"}, true}
   129  	}
   130  	if Q.Basis == "" && !strings.Contains(Q.Method, "3c") {
   131  		log.Printf("no basis set assigned for ORCA calculation, will used the default %s, \n", O.defbasis) //NOTE: This could be changed for a non-critical error
   132  		Q.Basis = O.defbasis
   133  	}
   134  	if Q.Method == "" {
   135  		log.Printf("no method assigned for ORCA calculation, will used the default %s, \n", O.defmethod)
   136  		Q.Method = O.defmethod
   137  		Q.auxColBasis = "" //makes no sense for pure functional
   138  		Q.auxBasis = fmt.Sprintf("%s/J", Q.Basis)
   139  	}
   140  
   141  	//Set RI or RIJCOSX if needed
   142  	ri := ""
   143  	if Q.RI && Q.RIJ {
   144  		return Error{"goChem/QM: RI and RIJ cannot be activated at the same time", Orca, O.inputname, "", []string{"BuildInput"}, true}
   145  	}
   146  	if Q.RI || Q.RIJ {
   147  		Q.auxBasis = "def2/J" //Of course, this will only work with Karlsruhe basis.
   148  		if O.orca3 {
   149  			Q.auxBasis = Q.Basis + "/J"
   150  		}
   151  		//	if !strings.Contains(Q.Others," RI "){
   152  		ri = "RI"
   153  	}
   154  	if Q.RIJ {
   155  		ri = "RIJCOSX"
   156  	}
   157  
   158  	disp := "D3"
   159  	if Q.Dispersion != "" {
   160  		disp = orcaDisp[Q.Dispersion]
   161  	}
   162  	opt := ""
   163  	trustradius := ""
   164  	jc := jobChoose{}
   165  	jc.opti = func() {
   166  		opt = "Opt"
   167  		trustradius = "%geom trust 0.3\nend\n\n" //Orca uses a fixed trust radius by default. This goChem makes an input that activates variable trust radius.
   168  	}
   169  	Q.Job.Do(jc)
   170  	//If this flag is set we'll look for a suitable MO file.
   171  	//If not found, we'll just use the default ORCA guess
   172  	hfuhf := "RHF"
   173  	if atoms.Multi() != 1 {
   174  		hfuhf = "UHF"
   175  	}
   176  	moinp := ""
   177  	if Q.OldMO == true {
   178  		dir, _ := os.Open("./")     //This should always work, hence ignoring the error
   179  		files, _ := dir.Readdir(-1) //Get all the files.
   180  		for _, val := range files {
   181  			if O.previousMO != "" {
   182  				break
   183  			}
   184  			if val.IsDir() == true {
   185  				continue
   186  			}
   187  			name := val.Name()
   188  			if strings.Contains(name, ".gbw") {
   189  				O.previousMO = name
   190  				break
   191  			}
   192  		}
   193  		if O.previousMO != "" {
   194  			//	Q.Guess = "MORead"
   195  			moinp = fmt.Sprintf("%%scf\n   Guess MORead\n   MOInp \"%s\"\nend\n\n", O.previousMO)
   196  		} else {
   197  			moinp = ""
   198  			//	Q.Guess = "" //The default guess
   199  		}
   200  	}
   201  	tight := "TightSCF"
   202  	if Q.SCFTightness != 0 {
   203  		tight = orcaSCFTight[Q.SCFTightness]
   204  	}
   205  	conv := ""
   206  	if Q.SCFConvHelp == 0 {
   207  		//The default for this is nothing for RHF and SlowConv for UHF
   208  		if atoms.Multi() > 1 {
   209  			conv = "SlowConv"
   210  		}
   211  	} else {
   212  		conv = orcaSCFConv[Q.SCFConvHelp]
   213  	}
   214  	pal := ""
   215  	if O.nCPU > 1 {
   216  		pal = fmt.Sprintf("%%pal nprocs %d\n   end\n\n", O.nCPU) //fmt.Fprintf(os.Stderr, "CPU number of %d for ORCA calculations currently not supported, maximun 8", O.nCPU)
   217  	}
   218  	grid := ""
   219  	if Q.Grid > 0 && Q.Grid <= 9 {
   220  		final := ""
   221  		if Q.Grid > 3 {
   222  			final = "NoFinalGrid"
   223  		}
   224  		grid = fmt.Sprintf("Grid%d %s", Q.Grid, final)
   225  	}
   226  	var err error
   227  	var bsse string
   228  	if bsse, err = O.buildgCP(Q); err != nil {
   229  		fmt.Fprintln(os.Stderr, err.Error())
   230  	}
   231  	HF3cAdditional := ""     // additional settings for HF-3c.
   232  	if Q.Method == "HF-3c" { //This method includes its own basis sets and corrections, so previous choices are overwritten. NOTE: there are some defaults that should be changed to get HF-3c to work better.
   233  		Q.Basis = ""
   234  		Q.auxBasis = ""
   235  		Q.auxColBasis = ""
   236  		Q.Guess = ""
   237  		bsse = ""
   238  		disp = ""
   239  		HF3cAdditional = "%scf\n   MaxIter 200\n   MaxIntMem 2000\nend\n\n"
   240  
   241  	}
   242  	MainOptions := []string{"!", hfuhf, Q.Method, Q.Basis, Q.auxBasis, Q.auxColBasis, tight, disp, conv, Q.Guess, opt, Q.Others, grid, ri, bsse, "\n\n"}
   243  	mainline := strings.Join(MainOptions, " ")
   244  	constraints := O.buildCConstraints(Q.CConstraints)
   245  	iconstraints, err := O.buildIConstraints(Q.IConstraints)
   246  	if err != nil {
   247  		return errDecorate(err, "BuildInput")
   248  	}
   249  	cosmo := ""
   250  	if Q.Dielectric > 0 {
   251  		method := "cpcm"
   252  		if O.orca3 {
   253  			method = "cosmo"
   254  		}
   255  		cosmo = fmt.Sprintf("%%%s epsilon %1.0f\n        refrac 1.30\n        end\n\n", method, Q.Dielectric)
   256  	}
   257  	mem := ""
   258  	if Q.Memory != 0 {
   259  		mem = fmt.Sprintf("%%MaxCore %d\n\n", Q.Memory)
   260  	}
   261  	ElementBasis := ""
   262  	if Q.HBElements != nil || Q.LBElements != nil {
   263  		elementbasis := make([]string, 0, len(Q.HBElements)+len(Q.LBElements)+2)
   264  		elementbasis = append(elementbasis, "%basis \n")
   265  		for _, val := range Q.HBElements {
   266  			elementbasis = append(elementbasis, fmt.Sprintf("  newgto %s \"%s\" end\n", val, Q.HighBasis))
   267  		}
   268  		for _, val := range Q.LBElements {
   269  			elementbasis = append(elementbasis, fmt.Sprintf("  newgto %s \"%s\" end\n", val, Q.LowBasis))
   270  		}
   271  		elementbasis = append(elementbasis, "         end\n\n")
   272  		ElementBasis = strings.Join(elementbasis, "")
   273  	}
   274  	//Now lets write the thing
   275  	if O.inputname == "" {
   276  		O.inputname = "gochem"
   277  	}
   278  	file, err := os.Create(fmt.Sprintf("%s.inp", O.wrkdir+O.inputname))
   279  	if err != nil {
   280  		return Error{ErrCantInput, Orca, O.inputname, err.Error(), []string{"os,Open", "BuildInput"}, true}
   281  
   282  	}
   283  	defer file.Close()
   284  	_, err = fmt.Fprint(file, mainline)
   285  	//With this check its assumed that the file is ok.https://www.reddit.com/
   286  	if err != nil {
   287  		return Error{ErrCantInput, Orca, O.inputname, err.Error(), []string{"fmt.Printf", "BuildInput"}, true}
   288  
   289  	}
   290  	//	fmt.Println("Ta wena la wea... chupa la callampa, uh uh uuuh", moinp) ///////////////
   291  	fmt.Fprint(file, HF3cAdditional)
   292  	fmt.Fprint(file, pal)
   293  	fmt.Fprint(file, moinp)
   294  	fmt.Fprint(file, mem)
   295  	fmt.Fprint(file, constraints)
   296  	fmt.Fprint(file, iconstraints)
   297  	fmt.Fprint(file, trustradius)
   298  	fmt.Fprint(file, ElementBasis)
   299  	fmt.Fprint(file, cosmo)
   300  	fmt.Fprint(file, "\n")
   301  	//Now the type of coords, charge and multiplicity
   302  	fmt.Fprintf(file, "* xyz %d %d\n", atoms.Charge(), atoms.Multi())
   303  	//now the coordinates
   304  	//	fmt.Println(atoms.Len(), coords.Rows()) ///////////////
   305  	for i := 0; i < atoms.Len(); i++ {
   306  		newbasis := ""
   307  		if isInInt(Q.HBAtoms, i) == true {
   308  			newbasis = fmt.Sprintf("newgto \"%s\" end", Q.HighBasis)
   309  		} else if isInInt(Q.LBAtoms, i) == true {
   310  			newbasis = fmt.Sprintf("newgto \"%s\" end", Q.LowBasis)
   311  		}
   312  		//	fmt.Println(atoms.Atom(i).Symbol)
   313  		fmt.Fprintf(file, "%-2s  %8.3f%8.3f%8.3f %s\n", atoms.Atom(i).Symbol, coords.At(i, 0), coords.At(i, 1), coords.At(i, 2), newbasis)
   314  	}
   315  	fmt.Fprintf(file, "*\n")
   316  	return nil
   317  }
   318  
   319  //Run runs the command given by the string O.command
   320  //it waits or not for the result depending on wait.
   321  //Not waiting for results works
   322  //only for unix-compatible systems, as it uses bash and nohup.
   323  func (O *OrcaHandle) Run(wait bool) (err error) {
   324  	if wait == true {
   325  		out, err := os.Create(fmt.Sprintf("%s.out", O.inputname))
   326  		if err != nil {
   327  			return Error{ErrNotRunning, Orca, O.inputname, "", []string{"Run"}, true}
   328  		}
   329  		defer out.Close()
   330  		command := exec.Command(O.command, fmt.Sprintf("%s.inp", O.inputname))
   331  		command.Stdout = out
   332  		command.Dir = O.wrkdir
   333  		err = command.Run()
   334  
   335  	} else {
   336  		command := exec.Command("sh", "-c", "nohup "+O.command+fmt.Sprintf(" %s.inp > %s.out &", O.inputname, O.inputname))
   337  		command.Dir = O.wrkdir
   338  		err = command.Start()
   339  	}
   340  	if err != nil {
   341  		err = Error{ErrNotRunning, Orca, O.inputname, err.Error(), []string{"exec.Start", "Run"}, true}
   342  	}
   343  	return err
   344  }
   345  
   346  //buildIConstraints transforms the list of cartesian constrains in the QMCalc structre
   347  //into a string with ORCA-formatted internal constraints.
   348  func (O *OrcaHandle) buildIConstraints(C []*IConstraint) (string, error) {
   349  	if C == nil {
   350  		return "\n", nil //no constraints
   351  	}
   352  	constraints := make([]string, len(C)+3)
   353  	constraints[0] = "%geom Constraints\n"
   354  	for key, val := range C {
   355  
   356  		if iConstraintOrder[val.Class] != len(val.CAtoms) {
   357  			return "", Error{"Internal constraint ill-formated", Orca, O.inputname, "", []string{"buildConstraints"}, true}
   358  		}
   359  
   360  		var temp string
   361  		var value string
   362  		//if UseVal is false, we don't add any value to the contraint. Orca will constraint the coordinate to its value in the starting structure.
   363  		if val.UseVal {
   364  			value = fmt.Sprintf("%2.3f", val.Val)
   365  		} else {
   366  			value = ""
   367  		}
   368  		if val.Class == 'B' {
   369  			temp = fmt.Sprintf("         {B %d %d %s C}\n", val.CAtoms[0], val.CAtoms[1], value)
   370  		} else if val.Class == 'A' {
   371  			temp = fmt.Sprintf("         {A %d %d %d %s C}\n", val.CAtoms[0], val.CAtoms[1], val.CAtoms[2], value)
   372  		} else if val.Class == 'D' {
   373  			temp = fmt.Sprintf("         {D %d %d %d %d %s C}\n", val.CAtoms[0], val.CAtoms[1], val.CAtoms[2], val.CAtoms[3], value)
   374  		}
   375  		constraints[key+1] = temp
   376  	}
   377  	last := len(constraints) - 1
   378  	constraints[last-1] = "         end\n"
   379  	constraints[last] = " end\n"
   380  	final := strings.Join(constraints, "")
   381  	return final, nil
   382  }
   383  
   384  var iConstraintOrder = map[byte]int{
   385  	'B': 2,
   386  	'A': 3,
   387  	'D': 4,
   388  }
   389  
   390  //buildCConstraints transforms the list of cartesian constrains in the QMCalc structre
   391  //into a string with ORCA-formatted cartesian constraints
   392  func (O *OrcaHandle) buildCConstraints(C []int) string {
   393  	if C == nil {
   394  		return "\n" //no constraints
   395  	}
   396  	constraints := make([]string, len(C)+3)
   397  	constraints[0] = "%geom Constraints\n"
   398  	for key, val := range C {
   399  		constraints[key+1] = fmt.Sprintf("         {C %d C}\n", val)
   400  	}
   401  	last := len(constraints) - 1
   402  	constraints[last-1] = "         end\n"
   403  	constraints[last] = " end\n"
   404  	final := strings.Join(constraints, "")
   405  	return final
   406  }
   407  
   408  //Only DFT is supported. Also, only Karlsruhe's basis sets. If you are using Pople's,
   409  //let us know so we can send a mission to rescue you from the sixties :-)
   410  func (O *OrcaHandle) buildgCP(Q *Calc) (string, error) {
   411  	ret := ""
   412  	var err error
   413  	if strings.ToLower(Q.BSSE) == "gcp" {
   414  		switch strings.ToLower(Q.Basis) {
   415  		case "def2-svp":
   416  			ret = "GCP(DFT/SVP)"
   417  		case "def2-tzvp":
   418  			ret = "GCP(DFT/TZ)"
   419  		case "def2-sv(p)":
   420  			ret = "GCP(DFT/SV(P))"
   421  		default:
   422  			err = Error{Orca, O.inputname, "Method/basis combination for gCP unavailable, will skip the correction", "", []string{"buildCP"}, false}
   423  		}
   424  	}
   425  	return ret, err
   426  }
   427  
   428  var orcaSCFTight = map[int]string{
   429  	0: "",
   430  	1: "TightSCF",
   431  	2: "VeryTightSCF",
   432  }
   433  
   434  var orcaSCFConv = map[int]string{
   435  	0: "",
   436  	1: "SlowConv",
   437  	2: "VerySlowConv",
   438  }
   439  
   440  var orcaDisp = map[string]string{
   441  	"nodisp": "",
   442  	"D3OLD":  "VDW10", //compatibility with ORCA 2.9
   443  	"D2":     "D2",
   444  	"D3BJ":   "D3BJ",
   445  	"D3bj":   "D3BJ",
   446  	"D3":     "D3ZERO",
   447  	"D3ZERO": "D3ZERO",
   448  	"D3Zero": "D3ZERO",
   449  	"D3zero": "D3ZERO",
   450  	"VV10":   "NL", //for these methods only the default integration grid is supported.
   451  	"SCVV10": "SCNL",
   452  	"NL":     "NL",
   453  	"SCNL":   "SCNL",
   454  }
   455  
   456  //OptimizedGeometry reads the latest geometry from an ORCA optimization. Returns the
   457  //  geometry or error. Returns the geometry AND error if the geometry read
   458  //  is not the product of a correctly ended ORCA calculation. In this case
   459  //  the error is "probable problem in calculation"
   460  func (O *OrcaHandle) OptimizedGeometry(atoms chem.Atomer) (*v3.Matrix, error) {
   461  	var err error
   462  	geofile := fmt.Sprintf("%s.xyz", O.wrkdir+O.inputname)
   463  	//Here any error of orcaNormal... or false means the same, so the error can be ignored.
   464  	if trust := O.orcaNormalTermination(); !trust {
   465  		err = Error{ErrProbableProblem, Orca, O.inputname, "", []string{"OptimizedGeometry"}, false}
   466  	}
   467  	//This might not be super efficient but oh well.
   468  	mol, err1 := chem.XYZFileRead(geofile)
   469  	if err1 != nil {
   470  		return nil, errDecorate(err1, "qm.OptimizedGeometry "+Orca+" "+O.inputname+" "+ErrNoGeometry) // Error{ErrNoEnergy, Orca, O.inputname, err1.Error(),[]string{"OptimizedGeometry"}, true}
   471  	}
   472  	return mol.Coords[0], err //returns the coords, the error indicates whether the structure is trusty (normal calculation) or not
   473  }
   474  
   475  //Energy returns the energy of a previous Orca calculations.
   476  //Returns error if problem, and also if the energy returned that is product of an
   477  //abnormally-terminated ORCA calculation. (in this case error is "Probable problem
   478  //in calculation")
   479  func (O *OrcaHandle) Energy() (float64, error) {
   480  	var err error
   481  	err = Error{ErrProbableProblem, Orca, O.inputname, "", []string{"Energy"}, false}
   482  	f, err1 := os.Open(fmt.Sprintf("%s.out", O.wrkdir+O.inputname))
   483  	if err1 != nil {
   484  		return 0, Error{ErrNoEnergy, Orca, O.inputname, err.Error(), []string{"os.Open", "qm.Energy"}, true}
   485  	}
   486  	defer f.Close()
   487  	f.Seek(-1, 2) //We start at the end of the file
   488  	energy := 0.0
   489  	var found bool
   490  	for i := 0; ; i++ {
   491  		line, err1 := getTailLine(f)
   492  		if err1 != nil {
   493  			return 0.0, errDecorate(err, "Energy "+O.inputname)
   494  		}
   495  		if strings.Contains(line, "**ORCA TERMINATED NORMALLY**") {
   496  			err = nil
   497  		}
   498  		if strings.Contains(line, "FINAL SINGLE POINT ENERGY") {
   499  			splitted := strings.Fields(line)
   500  			energy, err1 = strconv.ParseFloat(splitted[4], 64)
   501  			if err1 != nil {
   502  				return 0.0, Error{ErrNoEnergy, Orca, O.inputname, err1.Error(), []string{"strconv.Parsefloat", "Energy"}, true}
   503  
   504  			}
   505  			found = true
   506  			break
   507  		}
   508  	}
   509  	if !found {
   510  		return 0.0, Error{ErrNoEnergy, Orca, O.inputname, "", []string{"Energy"}, false}
   511  	}
   512  	return energy * chem.H2Kcal, err
   513  }
   514  
   515  //Gets previous line of the file f
   516  func getTailLine(f *os.File) (line string, err error) {
   517  	var i int64 = 1
   518  	buf := make([]byte, 1)
   519  	var ini int64 = -1
   520  	for ; ; i++ {
   521  		//move the pointer back one byte per cycle
   522  		if _, err := f.Seek(-2, 1); err != nil {
   523  			return "", Error{err.Error(), Orca, "Unknown", "", []string{"os.File.Seek", "getTailLine"}, true}
   524  		}
   525  		if _, err := f.Read(buf); err != nil {
   526  			return "", Error{err.Error(), Orca, "Unknown", "", []string{"os.File.Read", "getTailLine"}, true}
   527  		}
   528  		if buf[0] == byte('\n') && ini == -1 {
   529  			ini = i
   530  			break
   531  		}
   532  	}
   533  	bufF := make([]byte, ini)
   534  	f.Read(bufF)
   535  
   536  	if _, err := f.Seek(int64(-1*(len(bufF))), 1); err != nil { //making up for the read
   537  		return "", Error{err.Error(), Orca, "Unknown", "", []string{"os.File.Seek", "getTailLine"}, true}
   538  
   539  	}
   540  	return string(bufF), nil
   541  }
   542  
   543  //This checks that an ORCA calculation has terminated normally
   544  //I know this duplicates code, I wrote this one first and then the other one.
   545  func (O *OrcaHandle) orcaNormalTermination() bool {
   546  	var ini int64 = 0
   547  	var end int64 = 0
   548  	var first bool
   549  	buf := make([]byte, 1)
   550  	f, err := os.Open(fmt.Sprintf("%s.out", O.wrkdir+O.inputname))
   551  	if err != nil {
   552  		return false
   553  	}
   554  	defer f.Close()
   555  	var i int64 = 1
   556  	for ; ; i++ {
   557  		if _, err := f.Seek(-1*i, 2); err != nil {
   558  			return false
   559  		}
   560  		if _, err := f.Read(buf); err != nil {
   561  			return false
   562  		}
   563  		if buf[0] == byte('\n') && first == false {
   564  			first = true
   565  		} else if buf[0] == byte('\n') && end == 0 {
   566  			end = i
   567  		} else if buf[0] == byte('\n') && ini == 0 {
   568  			ini = i
   569  			break
   570  		}
   571  
   572  	}
   573  	f.Seek(-1*ini, 2)
   574  	bufF := make([]byte, ini-end)
   575  	f.Read(bufF)
   576  	if strings.Contains(string(bufF), "**ORCA TERMINATED NORMALLY**") {
   577  		return true
   578  	}
   579  	return false
   580  }