コード例 #1
0
ファイル: plane.go プロジェクト: rmera/examples_gochem
//This program will align the best plane passing through a set of atoms in a molecule with the XY-plane.
//Usage:
func main() {
	if len(os.Args) < 2 {
		fmt.Printf("Usage:\n%s file.xyz [indexes.dat]\nindexes.dat is a file containing one single line, with all the atoms defining the plane separated by spaces. If it is not given, all the atoms of the molecule will be taken to define the plane.\n", os.Args[0])
		os.Exit(1)
	}
	mol, err := chem.XYZFileRead(os.Args[1])
	if err != nil {
		panic(err.Error())
	}
	var indexes []int
	//if no file with indexes given, will just use all the atoms.
	if len(os.Args) < 3 {
		indexes = make([]int, mol.Len())
		for k, _ := range indexes {
			indexes[k] = k
		}
	} else {
		indexes, err = scu.IndexFileParse(os.Args[2])
		if err != nil {
			panic(err.Error())
		}
	}
	some := v3.Zeros(len(indexes)) //will contain the atoms selected to define the plane.
	some.SomeVecs(mol.Coords[0], indexes)
	//for most rotation things it is good to have the molecule centered on its mean.
	mol.Coords[0], _, _ = chem.MassCenter(mol.Coords[0], some, nil)
	//As we changed the atomic positions, must extract the plane-defining atoms again.
	some.SomeVecs(mol.Coords[0], indexes)
	//The strategy is: Take the normal to the plane of the molecule (os molecular subset), and rotate it until it matches the Z-axis
	//This will mean that the plane of the molecule will now match the XY-plane.
	best, err := chem.BestPlane(some, nil)
	if err != nil {
		panic(err.Error())
	}
	z, _ := v3.NewMatrix([]float64{0, 0, 1})
	zero, _ := v3.NewMatrix([]float64{0, 0, 0})
	fmt.Fprintln(os.Stderr, "Best  Plane", best, z, indexes)
	axis := v3.Zeros(1)
	axis.Cross(best, z)
	fmt.Fprintln(os.Stderr, "axis", axis)
	//The main part of the program, where the rotation actually happens. Note that we rotate the whole
	//molecule, not just the planar subset, this is only used to calculate the rotation angle.
	mol.Coords[0], err = chem.RotateAbout(mol.Coords[0], zero, axis, chem.Angle(best, z))
	if err != nil {
		panic(err.Error())
	}
	//Now we write the rotated result.
	final, err := chem.XYZStringWrite(mol.Coords[0], mol)
	fmt.Print(final)
	fmt.Fprintln(os.Stderr, err)
}
コード例 #2
0
ファイル: geometric.go プロジェクト: rmera/gochem
//RotatorAroundZ returns an operator that will rotate a set of
//coordinates by gamma radians around the z axis.
func RotatorAroundZ(gamma float64) (*v3.Matrix, error) {
	singamma := math.Sin(gamma)
	cosgamma := math.Cos(gamma)
	operator := []float64{cosgamma, singamma, 0,
		-singamma, cosgamma, 0,
		0, 0, 1}
	return v3.NewMatrix(operator)

}
コード例 #3
0
ファイル: files.go プロジェクト: rmera/gochem
//xyzReadSnap reads an xyz file snapshot from a bufio.Reader, returns a slice of Atom objects, which will be nil if ReadTopol is false,
// a slice of matrix.DenseMatrix and an error or nil.
func xyzReadSnap(xyz *bufio.Reader, ReadTopol bool) (*v3.Matrix, []*Atom, error) {
	line, err := xyz.ReadString('\n')
	if err != nil {
		return nil, nil, CError{fmt.Sprintf("Empty XYZ File: %s", err.Error()), []string{"bufio.Reader.ReadString", "xyzReadSnap"}}
	}
	natoms, err := strconv.Atoi(strings.TrimSpace(line))
	if err != nil {
		return nil, nil, CError{fmt.Sprintf("Wrong header for an XYZ file %s", err.Error()), []string{"strconv.Atoi", "xyzReadSnap"}}
	}
	var molecule []*Atom
	if ReadTopol {
		molecule = make([]*Atom, natoms, natoms)
	}
	coords := make([]float64, natoms*3, natoms*3)
	_, err = xyz.ReadString('\n') //We dont care about this line
	if err != nil {
		return nil, nil, CError{fmt.Sprintf("Ill formatted XYZ file: %s", err.Error()), []string{"bufio.Reader.ReadString", "xyzReadSnap"}}

	}
	errs := make([]error, 3, 3)
	for i := 0; i < natoms; i++ {
		line, errs[0] = xyz.ReadString('\n')
		if errs[0] != nil { //inefficient, (errs[1] can be checked once before), but clearer.
			if strings.Contains(errs[0].Error(), "EOF") && i == natoms-1 { //This allows that an XYZ ends without a newline
				errs[0] = nil
			} else {
				break
			}
		}
		fields := strings.Fields(line)
		if len(fields) < 4 {
			errs[0] = fmt.Errorf("Line number %d ill formed", i)
			break
		}
		if ReadTopol {
			molecule[i] = new(Atom)
			molecule[i].Symbol = strings.Title(fields[0])
			molecule[i].Mass = symbolMass[molecule[i].Symbol]
			molecule[i].Molname = "UNK"
			molecule[i].Name = molecule[i].Symbol
		}
		coords[i*3], errs[0] = strconv.ParseFloat(fields[1], 64)
		coords[i*3+1], errs[1] = strconv.ParseFloat(fields[2], 64)
		coords[i*3+2], errs[2] = strconv.ParseFloat(fields[3], 64)
	}
	//This could be done faster if done in the same loop where the coords are read
	//Instead of having another loop just for them.
	for k, i := range errs {
		if i != nil {
			fmt.Println("line", line, k)
			return nil, nil, CError{i.Error(), []string{"strconv.ParseFloat", "xyzReadSnap"}}
		}
	}
	mcoords, err := v3.NewMatrix(coords)
	return mcoords, molecule, errDecorate(err, "xyzReadSnap")
}
コード例 #4
0
ファイル: gochem_test.go プロジェクト: rmera/gochem
func TestProjectionAndAntiProjection(Te *testing.T) {
	A := v3.Zeros(1)
	A.Set(0, 0, 2.0)
	B, _ := v3.NewMatrix([]float64{1, 1, 0})
	C := AntiProjection(A, B)
	D := Projection(B, A)
	fmt.Println("Projection of B on A (D)", D)
	fmt.Println("Anti-projection of A on B (C):", C)
	fmt.Println("Norm of C: ", C.Norm(0), " Norm of A,B: ", A.Norm(0), B.Norm(0), "Norm of D:", D.Norm(0))
}
コード例 #5
0
ファイル: gochem_test.go プロジェクト: rmera/gochem
//Aligns the main plane of a molecule with the XY-plane.
//Here XYZRead and XYZWrite are tested
func TestPutInXYPlane(Te *testing.T) {
	myxyz, _ := os.Open("test/sample_plane.xyz")
	mol, err := XYZRead(myxyz)
	if err != nil {
		Te.Error(err)
	}
	indexes := []int{0, 1, 2, 3, 23, 22, 21, 20, 25, 44, 39, 40, 41, 42, 61, 60, 59, 58, 63, 5}
	some := v3.Zeros(len(indexes))
	some.SomeVecs(mol.Coords[0], indexes)
	//for most rotation things it is good to have the molecule centered on its mean.
	mol.Coords[0], _, _ = MassCenter(mol.Coords[0], some, nil)
	//The test molecule is not completely planar so we use a subset of atoms that are contained in a plane
	//These are the atoms given in the indexes slice.
	some.SomeVecs(mol.Coords[0], indexes)
	//The strategy is: Take the normal to the plane of the molecule (os molecular subset), and rotate it until it matches the Z-axis
	//This will mean that the plane of the molecule will now match the XY-plane.
	best, err := BestPlane(some, nil)
	if err != nil {
		err2 := err.(Error)
		fmt.Println(err2.Decorate(""))
		Te.Fatal(err)
		//		panic(err.Error())
	}
	z, _ := v3.NewMatrix([]float64{0, 0, 1})
	zero, _ := v3.NewMatrix([]float64{0, 0, 0})
	fmt.Println("Best  Plane", best, z)
	axis := v3.Zeros(1)
	axis.Cross(best, z)
	fmt.Println("axis", axis)
	//The main part of the program, where the rotation actually happens. Note that we rotate the whole
	//molecule, not just the planar subset, this is only used to calculate the rotation angle.
	//	fmt.Println("DATA", mol.Coords[0], zero, axis, Angle(best, z))
	mol.Coords[0], err = RotateAbout(mol.Coords[0], zero, axis, Angle(best, z))
	if err != nil {
		Te.Error(err)
	}
	//	fmt.Println("after!", mol.Coords[0], err)
	//Now we write the rotated result.
	outxyz, _ := os.Create("test/Rotated.xyz") //This is the XYZWrite written file
	XYZWrite(outxyz, mol.Coords[0], mol)
}
コード例 #6
0
ファイル: json.go プロジェクト: rmera/gochem
//DecodeMolecule Decodes a JSON molecule into a gochem molecule. Can handle several frames (all of which need to have the same amount of atoms). It does
//not collect the b-factors.
func DecodeMolecule(stream *bufio.Reader, atomnumber, frames int) (*chem.Topology, []*v3.Matrix, *Error) {
	const funcname = "DecodeMolecule" //for the error
	atoms := make([]*chem.Atom, 0, atomnumber)
	coordset := make([]*v3.Matrix, 0, frames)
	rawcoords := make([]float64, 0, 3*atomnumber)
	for i := 0; i < atomnumber; i++ {
		line, err := stream.ReadBytes('\n') //Using this function allocates a lot without need. There is no function that takes a []bytes AND a limit. I might write one at some point.
		if err != nil {
			break
		}
		at := new(chem.Atom)
		err = json.Unmarshal(line, at)
		if err != nil {
			return nil, nil, NewError("selection", funcname, err)
		}
		atoms = append(atoms, at)
		line, err = stream.ReadBytes('\n') //See previous comment.
		if err != nil {
			break
		}
		ctemp := new(Coords)
		if err = json.Unmarshal(line, ctemp); err != nil {
			return nil, nil, NewError("selection", funcname, err)
		}
		rawcoords = append(rawcoords, ctemp.Coords...)
	}
	mol := chem.NewTopology(atoms, -1, 99999) //no idea of the charge or multiplicity
	coords, err := v3.NewMatrix(rawcoords)
	if err != nil {
		return nil, nil, NewError("selection", funcname, err)
	}
	coordset = append(coordset, coords)
	if frames == 1 {
		return mol, coordset, nil
	}
	for i := 0; i < (frames - 1); i++ {
		coords, err := DecodeCoords(stream, atomnumber)
		if err != nil {
			return mol, coordset, NewError("selection", funcname, fmt.Errorf("Error reading the %d th frame: %s", i+2, err.Error()))
		}
		coordset = append(coordset, coords)
	}
	return mol, coordset, nil

}
コード例 #7
0
ファイル: geometric.go プロジェクト: rmera/gochem
//RotatorToNewY takes a set of coordinates (mol) and a vector (y). It returns
//a rotation matrix that, when applied to mol, will rotate it around the Z axis
//in such a way that the projection of newy in the XY plane will be aligned with
//the Y axis.
func RotatorAroundZToNewY(newy *v3.Matrix) (*v3.Matrix, error) {
	nr, nc := newy.Dims()
	if nc != 3 || nr != 1 {
		return nil, CError{"Wrong newy vector", []string{"RotatorAroundZtoNewY"}}
	}
	if nc != 3 {
		return nil, CError{"Wrong mol vector", []string{"RotatorAroundZtoNewY"}} //this one doesn't seem reachable

	}
	gamma := math.Atan2(newy.At(0, 0), newy.At(0, 1))
	singamma := math.Sin(gamma)
	cosgamma := math.Cos(gamma)
	operator := []float64{cosgamma, singamma, 0,
		-singamma, cosgamma, 0,
		0, 0, 1}
	return v3.NewMatrix(operator)

}
コード例 #8
0
ファイル: gochem_test.go プロジェクト: rmera/gochem
//TestChangeAxis reads the PDB 2c9v.pdb from the test directory, collects
//The CA and CB of residue D124 of the chain A, and uses Clifford algebra to rotate the
//whole molecule such as the vector defined by these 2 atoms is
//aligned with the Z axis. The new molecule is written
//as 2c9v_aligned.pdb to the test folder.
func TestChangeAxis(Te *testing.T) {
	//runtime.GOMAXPROCS(2) ///////////////////////////
	mol, err := PDBFileRead("test/2c9v.pdb", true)
	if err != nil {
		Te.Error(err)
	}
	PDBFileWrite("test/2c9v-Readtest.pdb", mol.Coords[0], mol, nil)
	//The selection thing
	orient_atoms := [2]int{0, 0}
	for index := 0; index < mol.Len(); index++ {
		atom := mol.Atom(index)
		if atom.Chain == "A" && atom.MolID == 124 {
			if atom.Name == "CA" {
				orient_atoms[0] = index
			} else if atom.Name == "CB" {
				orient_atoms[1] = index
			}
		}
	}
	//Get the axis of rotation
	//ov1:=mol.Coord(orient_atoms[0], 0)
	ov2 := mol.Coord(orient_atoms[1], 0)
	//now we center the thing in the beta carbon of D124
	mol.Coords[0].SubVec(mol.Coords[0], ov2)
	PDBFileWrite("test/2c9v-translated.pdb", mol.Coords[0], mol, nil)
	//Now the rotation
	ov1 := mol.Coord(orient_atoms[0], 0) //make sure we have the correct versions
	ov2 = mol.Coord(orient_atoms[1], 0)  //same
	orient := v3.Zeros(ov2.NVecs())
	orient.Sub(ov2, ov1)
	//	PDBWrite(mol,"test/2c9v-124centered.pdb")
	Z, _ := v3.NewMatrix([]float64{0, 0, 1})
	axis := cross(orient, Z)
	angle := Angle(orient, Z)
	oldcoords := v3.Zeros(mol.Coords[0].NVecs())
	oldcoords.Copy(mol.Coords[0])
	mol.Coords[0] = Rotate(oldcoords, mol.Coords[0], axis, angle)
	if err != nil {
		Te.Error(err)
	}
	PDBFileWrite("test/2c9v-aligned.pdb", mol.Coords[0], mol, nil)
	fmt.Println("bench1")
}
コード例 #9
0
ファイル: json.go プロジェクト: rmera/gochem
//Decodecoords decodes streams from a bufio.Reader containing 3*atomnumber JSON floats into a v3.Matrix with atomnumber rows.
func DecodeCoords(stream *bufio.Reader, atomnumber int) (*v3.Matrix, *Error) {
	const funcname = "DecodeCoords"
	rawcoords := make([]float64, 0, 3*atomnumber)
	for i := 0; i < atomnumber; i++ {
		line, err := stream.ReadBytes('\n') //Using this function allocates a lot without need. There is no function that takes a []bytes AND a limit. I might write one at some point.
		if err != nil {
			break
		}
		ctemp := new(Coords)
		if err = json.Unmarshal(line, ctemp); err != nil {
			return nil, NewError("selection", funcname, err)
		}
		rawcoords = append(rawcoords, ctemp.Coords...)
	}
	coords, err := v3.NewMatrix(rawcoords)
	if err != nil {
		return nil, NewError("selection", funcname, err)
	}
	return coords, nil
}
コード例 #10
0
ファイル: geometric.go プロジェクト: rmera/gochem
//RotatorToNewZ takes a matrix a row vector (newz).
//It returns a linear operator such that, when applied to a matrix mol ( with the operator on the right side)
//it will rotate mol such that the z axis is aligned with newz.
func RotatorToNewZ(newz *v3.Matrix) *v3.Matrix {
	r, c := newz.Dims()
	if c != 3 || r != 1 {
		panic("Wrong newz vector")
	}
	normxy := math.Sqrt(math.Pow(newz.At(0, 0), 2) + math.Pow(newz.At(0, 1), 2))
	theta := math.Atan2(normxy, newz.At(0, 2))      //Around the new y
	phi := math.Atan2(newz.At(0, 1), newz.At(0, 0)) //First around z
	psi := 0.000000000000                           // second around z
	sinphi := math.Sin(phi)
	cosphi := math.Cos(phi)
	sintheta := math.Sin(theta)
	costheta := math.Cos(theta)
	sinpsi := math.Sin(psi)
	cospsi := math.Cos(psi)
	operator := []float64{cosphi*costheta*cospsi - sinphi*sinpsi, -sinphi*cospsi - cosphi*costheta*sinpsi, cosphi * sintheta,
		sinphi*costheta*cospsi + cosphi*sinpsi, -sinphi*costheta*sinpsi + cosphi*cospsi, sintheta * sinphi,
		-sintheta * cospsi, sintheta * sinpsi, costheta}
	finalop, _ := v3.NewMatrix(operator) //we are hardcoding opperator so it must have the right dimensions.
	return finalop

}
コード例 #11
0
ファイル: handy.go プロジェクト: cornerot/gochem
//MakeWater Creates a water molecule at distance Angstroms from a2, in a direction that is angle radians from the axis defined by a1 and a2.
//Notice that the exact position of the water is not well defined when angle is not zero. One can always use the RotateAbout
//function to move the molecule to the desired location. If oxygen is true, the oxygen will be pointing to a2. Otherwise,
//one of the hydrogens will.
func MakeWater(a1, a2 *v3.Matrix, distance, angle float64, oxygen bool) *v3.Matrix {
	water := v3.Zeros(3)
	const WaterOHDist = 0.96
	const WaterAngle = 52.25
	const deg2rad = 0.0174533
	w := water.VecView(0) //we first set the O coordinates
	w.Copy(a2)
	w.Sub(w, a1)
	w.Unit(w)
	dist := v3.Zeros(1)
	dist.Sub(a1, a2)
	a1a2dist := dist.Norm(0)
	fmt.Println("ala2dist", a1a2dist, distance) ////////////////7777
	w.Scale(distance+a1a2dist, w)
	w.Add(w, a1)
	for i := 0; i <= 1; i++ {
		o := water.VecView(0)
		w = water.VecView(i + 1)
		w.Copy(o)
		fmt.Println("w1", w) ////////
		w.Sub(w, a2)
		fmt.Println("w12", w) ///////////////
		w.Unit(w)
		fmt.Println("w4", w)
		w.Scale(WaterOHDist+distance, w)
		fmt.Println("w3", w, WaterOHDist, distance)
		o.Sub(o, a2)
		t, _ := v3.NewMatrix([]float64{0, 0, 1})
		upp := v3.Zeros(1)
		upp.Cross(w, t)
		fmt.Println("upp", upp, w, t)
		upp.Add(upp, o)
		upp.Add(upp, a2)
		//water.SetMatrix(3,0,upp)
		w.Add(w, a2)
		o.Add(o, a2)
		sign := 1.0
		if i == 1 {
			sign = -1.0
		}
		temp, _ := RotateAbout(w, o, upp, deg2rad*WaterAngle*sign)
		w.SetMatrix(0, 0, temp)
	}
	var v1, v2 *v3.Matrix
	if angle != 0 {
		v1 = v3.Zeros(1)
		v2 = v3.Zeros(1)
		v1.Sub(a2, a1)
		v2.Copy(v1)
		v2.Set(0, 2, v2.At(0, 2)+1) //a "random" modification. The idea is that its not colinear with v1
		v3 := cross(v1, v2)
		v3.Add(v3, a2)
		water, _ = RotateAbout(water, a2, v3, angle)
	}
	if oxygen {
		return water
	}
	//we move things so an hydrogen points to a2 and modify the distance acordingly.
	e1 := water.VecView(0)
	e2 := water.VecView(1)
	e3 := water.VecView(2)
	if v1 == nil {
		v1 = v3.Zeros(1)
	}
	if v2 == nil {
		v2 = v3.Zeros(1)
	}
	v1.Sub(e2, e1)
	v2.Sub(e3, e1)
	axis := cross(v1, v2)
	axis.Add(axis, e1)
	water, _ = RotateAbout(water, e1, axis, deg2rad*(180-WaterAngle))
	v1.Sub(e1, a2)
	v1.Unit(v1)
	v1.Scale(WaterOHDist, v1)
	water.AddVec(water, v1)
	return water
}
コード例 #12
0
ファイル: files.go プロジェクト: rmera/gochem
//pdbBufIORead reads the atomic entries for a PDB bufio.IO, returns a bunch of without coordinates,
// and the coordinates in a separate array of arrays. If there is one frame in the PDB
// the coordinates array will be of lenght 1. It also returns an error which is not
// really well set up right now.
func pdbBufIORead(pdb *bufio.Reader, read_additional bool) (*Molecule, error) {
	molecule := make([]*Atom, 0)
	modelnumber := 0 //This is the number of frames read
	coords := make([][]float64, 1, 1)
	coords[0] = make([]float64, 0)
	bfactors := make([][]float64, 1, 1)
	bfactors[0] = make([]float64, 0)
	first_model := true //are we reading the first model? if not we only save coordinates
	contlines := 1      //count the lines read to better report errors
	for {
		line, err := pdb.ReadString('\n')
		if err != nil {
			//fmt.Println("PDB reading complete") /***change this to stderr************/
			break
			contlines++ //count all the lines even if empty.
		}
		if len(line) < 4 {
			continue
		}
		//here we start actually reading
		/*There might be a bug for not copying the string (symbol, name, etc) but just assigning the slice
		 * which is a reference. Check!*/
		var c = make([]float64, 3, 3)
		var bfactemp float64 //temporary bfactor
		var atomtmp *Atom
		//	var foo string // not really needed
		if strings.HasPrefix(line, "ATOM") || strings.HasPrefix(line, "HETATM") {
			if !first_model {
				c, bfactemp, err = read_onlycoords_pdb_line(line, contlines)
				if err != nil {
					return nil, errDecorate(err, "pdbBufIORead")
				}
			} else {
				atomtmp = new(Atom)
				atomtmp, c, bfactemp, err = read_full_pdb_line(line, read_additional, contlines)
				if err != nil {
					return nil, errDecorate(err, "pdbBufIORead")
				}
				//atom data other than coords is the same in all models so just read for the first.
				molecule = append(molecule, atomtmp)
			}
			//coords are appended for all the models
			//we add the coords to the latest frame of coordinaates
			coords[len(coords)-1] = append(coords[len(coords)-1], c[0], c[1], c[2])
			bfactors[len(bfactors)-1] = append(bfactors[len(bfactors)-1], bfactemp)
		} else if strings.HasPrefix(line, "MODEL") {
			modelnumber++        //,_=strconv.Atoi(strings.TrimSpace(line[6:]))
			if modelnumber > 1 { //will be one for the first model, 2 for the second.
				first_model = false
				coords = append(coords, make([]float64, 0)) //new bunch of coords for a new frame
				bfactors = append(bfactors, make([]float64, 0))
			}
		}
	}
	//This could be done faster if done in the same loop where the coords are read
	//Instead of having another loop just for them.
	top := NewTopology(molecule, 0, 1)
	var err error
	frames := len(coords)
	mcoords := make([]*v3.Matrix, frames, frames) //Final thing to return
	for i := 0; i < frames; i++ {
		mcoords[i], err = v3.NewMatrix(coords[i])
		if err != nil {
			return nil, errDecorate(err, "pdbBufIORead")
		}
	}
	//if something happened during the process
	if err != nil {
		return nil, errDecorate(err, "pdbBufIORead")
	}
	returned, err := NewMolecule(mcoords, top, bfactors)
	return returned, errDecorate(err, "pdbBufIORead")
}
コード例 #13
0
ファイル: mopac.go プロジェクト: rmera/gochem
/*Get Geometry reads the optimized geometry from a MOPAC2009/2012 output.
  Return error if fail. Returns Error ("Probable problem in calculation")
  if there is a geometry but the calculation didnt end properly*/
func (O *MopacHandle) OptimizedGeometry(atoms chem.Atomer) (*v3.Matrix, error) {
	var err error
	natoms := atoms.Len()
	coords := make([]float64, natoms*3, natoms*3) //will be used for return
	file, err := os.Open(fmt.Sprintf("%s.out", O.inputname))
	if err != nil {
		return nil, Error{ErrNoGeometry, Mopac, O.inputname, "", []string{"os.Open", "OptimizedGeometry"}, true}
	}
	defer file.Close()
	out := bufio.NewReader(file)
	err = Error{ErrNoGeometry, Mopac, O.inputname, "", []string{"OptimizedGeometry"}, true}
	//some variables that will be changed/increased during the next for loop
	final_point := false //to see if we got to the right part of the file
	reading := false     //start reading
	i := 0
	errsl := make([]error, 3, 3)
	trust_radius_warning := false
	for {
		var line string
		line, err = out.ReadString('\n')
		if err != nil {
			break
		}

		if (!reading) && strings.Contains(line, "TRUST RADIUS NOW LESS THAN 0.00010 OPTIMIZATION TERMINATING") {
			trust_radius_warning = true
			continue
		}

		//MOPAC output is a pleasure to parse. IF YOU ARE A F*** PERKELEN CTM M*******T!!!!!!!!!!!!!!!!!!!
		if !reading && (strings.Contains(line, "FINAL  POINT  AND  DERIVATIVES") || strings.Contains(line, "GEOMETRY OPTIMISED")) || strings.Contains(line, "GRADIENTS WERE INITIALLY ACCEPTABLY SMALL") {
			final_point = true
			continue
		}
		if strings.Contains(line, "(ANGSTROMS)     (ANGSTROMS)     (ANGSTROMS)") && final_point {
			_, err = out.ReadString('\n')
			if err != nil {
				break
			}
			reading = true
			continue
		}
		if reading {
			//So far we dont check that there are not too many atoms in the mopac output.
			if i >= natoms {
				err = nil
				break
			}
			coords[i*3], errsl[0] = strconv.ParseFloat(strings.TrimSpace(line[22:35]), 64)
			coords[i*3+1], errsl[1] = strconv.ParseFloat(strings.TrimSpace(line[38:51]), 64)
			coords[i*3+2], errsl[2] = strconv.ParseFloat(strings.TrimSpace(line[54:67]), 64)
			i++
			err = parseErrorSlice(errsl)
			if err != nil {
				break
			}
		}
	}
	if err != nil {
		return nil, Error{ErrNoGeometry, Mopac, O.inputname, err.Error(), []string{"OptimizedGeometry"}, true}
	}
	mcoords, err := v3.NewMatrix(coords)
	if trust_radius_warning {
		return mcoords, Error{ErrProbableProblem, Mopac, O.inputname, "", []string{"OptimizedGeometry"}, false}
	}
	return mcoords, err
}