7
$\begingroup$

I have seen the other posts relating to this however there seems to be more restrictions when exporting to .XYZ. I am pretty new to this but I have been searching for hours. Basically, I have a $n\times 3$ matrix:

matrix = {VertexTypes->{C, N, C}, 
          VertexCoordinates->{
            {-0.447094, 0.754888, -1.66937},
            {0.625759, 2.75335, -2.19647},
            {1.76312, 0.724608, -2.34303}
           }
          }

and after exporting it changes to scientific notation:

Export["hey.xyz", matrix]

I have tried AccountingForm and a few other written codes on here however also I am a beginner so I am not sure how to incorporate these into my Export. Secondly, I need the numbers to remain in Angstrom and not converted to picometer. If this could be prohibited I believe this would solve the problem of the introduced scientific notation which is introduced upon conversion.

$\endgroup$
1
  • 1
    $\begingroup$ there is some documentation for the XYZ format import/export. I have not studied it in detail, don't know the XYZ format and format documentation is generally rather poor, but I think you will need at least using string instead of symbols ("VertexTypes" vs. VertexTypes) and als use "Rules" for the element description. I would play around with the examples in the documentation (/ref/format/XYZ in the documentation), that usually gives you a feeling about what works... $\endgroup$ May 9, 2015 at 10:02

2 Answers 2

3
$\begingroup$

UPDATE: an export routine written from scratch

After clarifications from Zoe, I realize that her problem is not in the export format; not even in the sneaky way that Mathematica tries to outsmart the user by performing hidden unit conversions. The problem is the output number format!

I couldn't find a way to convince the Export function to output numbers in a format of my choosing. Fortunately, however, the XYZ file format is particularly simple, so I was able to cobble together an ad hoc export routine, shown below. The variables are hopefully self-explanatory.

Function definition

exportXYZ::usage = 
  "\nFunction called with incorrect parameters. Syntax is:\n exportXYZ[ filepath, { comment, vertexlist, atom coordinate list } ]";

exportXYZ::mismatch = 
  "The dimensions of the vertex list and the list of atomic coordinates do not match.";

exportXYZ[filepath_, {comment_, vertices_, atomcoordinates_}] :=
 Module[
  {outputfilestream, outputstring},

  If[
   Or[Not@StringQ[filepath], Not@StringQ[comment]],
   Message[exportXYZ::usage]; Abort[]
  ];

  If[Length@vertices != First@Dimensions@atomcoordinates,
   Message[exportXYZ::mismatch]; Abort[]
  ];

  outputstring =
   ToString@Length[vertices] <> "\n" <>
    comment <> "\n" <>
    MapThread[
     (#1 <> "\t" <> ExportString[{#2}, "TSV"] <> "\n" &),
     {vertices, atomcoordinates}
     ];

  outputfilestream = OpenWrite[filepath];
  WriteString[outputfilestream, outputstring];
  Close[outputfilestream];

  Return[outputstring]
]

Using the function:

(* Building blocks *)
comment = "This text will appear in the second line of the generated file";
vertices = {"C", "N", "C"};
atomcoordinates = {
   {-0.447094, 0.754888, -1.66937},
   {0.625759, 2.75335, -2.19647},
   {1.76312, 0.724608, -2.34303}
   };

exportXYZ["firstout.xyz", {comment, vertices, atomcoordinates}]

(* Out:
"3
This text will appear in the second line of the generated file
C   -0.447094   0.754888    -1.66937
N   0.625759    2.75335 -2.19647
C   1.76312 0.724608    -2.34303
"
*)

Hopefully this will be more helpful. A further step will be to write an export function that takes those values and neatly writes them into a file, so that it won't require manually adjusting e.g. the file path etc.


Original answer:

Formatting for export is one of the most obscure sections of the documentation. What I know comes from an example in the Mathematica documentation of the .XYZ file format. Look for the last bullet point under the "Elements" heading.

Mathematica is performing an implicit unit conversion on your input. You can find in the documentation page that Mathematica interprets the values of the coordinates fed to the export function as expressed in picometers. On the other hand, the common convention for the XYZ file format is to express the coordinates in angstrom instead! Therein lies the source of the confusion: for example, your first coordinate of $-0.44$ is interpreted as -0.44 picometers (=$-0.44 \times 10^{-12}\ \textrm{m}$), translated to $-0.0044 \times 10^{-10}\ \textrm{m} = -0.0044 \ \textrm{Angstrom}$. This is why -4.4E-3 is written to the output file.

Export syntax: Below is the format I use, adapted for your simple dataset, which seems to work. I have added a few comments to make the construction of the export format more clear.

Export[
 (* file name *)
 "hey.xyz",

 (* Data to export *)
 {
  (* List of vertices *)
  {"C", "N", "C"},

  (* List of coordinates *)
  {
    {-0.447094, 0.754888, -1.66937}, 
    {0.625759, 2.75335, -2.19647}, 
    {1.76312, 0.724608, -2.34303}
  }
 },

 (* Format specifications *)
 {"XYZ", {"VertexTypes", "VertexCoordinates"}}
]

The contents of the exported file are shown below. The numbers and element labels are tab separated.

3
Created with the Wolfram Language : www.wolfram.com
C   -4.47094E-3  7.54888E-3 -1.66937E-2
N    6.25759E-3  2.75335E-2 -2.19647E-2
C    1.76312E-2  7.24608E-3 -2.34303E-2
$\endgroup$
4
  • $\begingroup$ Yes, but the program I am running these files into cannot read the E (exp) and secondly reads in Angstrom NOT picometers. Herein lies my problem. I realise now I was not explicit enough in my question. I need full decimal form and not scientific notation. $\endgroup$
    – Zoe
    May 11, 2015 at 0:45
  • $\begingroup$ @Zoe I see. I misunderstood. I can't think of a way of convincing Export to do that, so I wrote code to construct the output file directly from the format specifications. I haven't tested all "corner cases" though, so could you try out with some real data, if you have some at hand, and let me know how it works out for you? $\endgroup$
    – MarcoB
    May 11, 2015 at 2:07
  • $\begingroup$ This is excellent. However, I'm not sure how to use the filepath variable. Or is that just the name of the output? $\endgroup$
    – Zoe
    May 13, 2015 at 4:40
  • $\begingroup$ @Zoe Glad to be of help! And you are right, filepath is just a string in quotes that contains the name (and, if you want, the full path) of the output file you want to generate. If you don't specify a full path, MMA will typically export files to your Documents folder (check this by evaluating $UserDocumentsDirectory in MMA). If you do want to specify a full path, you have to escape the backslashes: for instance, if I wanted to save a file named "out.xyz" on the desktop on my Win 7 computer I would use the following string as filepath: "C:\\Users\\Marco\\Desktop\\out.xyz" . $\endgroup$
    – MarcoB
    May 13, 2015 at 4:55
2
$\begingroup$

(Updated for clarity)

Skimming through Developing an Export Filter is helpful in understanding the process I describe below.

First, it is helpful to see what Mathematica is doing when it exports an XYZ file. To figure this out, we can look at the appropriate formatting package:

FileNameJoin[{$InstallationDirectory, "SystemFiles", "Formats", "XYZ", "Export.m"}] // FilePrint

The package looks like this:

(* ::Package:: *)

ImportExport`RegisterExport[
  "XYZ",
  System`Convert`XYZDump`ExportXYZ,
  "FunctionChannels" -> {"Streams"}
]

If we import an example XYZ file, that sets the appropriate contexts for the export filter and we can get access to the code that is used to perform the conversion:

c = Import["ExampleData/caffeine.xyz", {{"VertexTypes", "VertexCoordinates"}}];
?System`Convert`XYZDump`ExportXYZ

Mathematica graphics

Taking a closer look at this code, we note that PaddedForm is applied to the data:

Mathematica graphics

I'd like to create a modified export filter that maintains the functionality of the system filter but avoids the options in PaddedForm that generate the scientific notation. Perhaps the easiest way to do this is:

  • Copy and paste the Definition text
  • Rename the functions and remove reference to private contexts
  • Modify the code to suit our purposes
  • Register the new Export filter

The first three steps result in the code below:

myxyzexporter[strm_, rules_, opts___?OptionQ] := 
 Module[{elenames, atoms, coords, nobasicinput, data}, 
   ImportExport`MoleculePlot3D;
   elenames = System`ConvertersDump`Utilities`ElementNames[rules];
   If[FreeQ[elenames, "VertexTypes"] || 
     FreeQ[elenames, "VertexCoordinates"], 
    Message[Export::inselem, "XYZ"]; Return[$Failed]];
   atoms = "VertexTypes" /. rules;
   coords = "VertexCoordinates" /. rules; nobasicinput = {};
   If[Length[atoms] < 1, 
    nobasicinput = Append[nobasicinput, "VertexTypes"]];
   If[Length[coords] < 1, 
    nobasicinput = Append[nobasicinput, "VertexCoordinates"]];
   If[nobasicinput =!= {}, 
    Message[Export::nodta, nobasicinput, "XYZ"]; Return[$Failed]];
   If[Length[atoms] =!= Length[coords], 
    Message[Export::uneqlen, "VertexCoordinates", "VertexTypes"];
    Return[$Failed]];
   If[! ArrayQ[coords, 2, NumericQ] || Length[coords[[1]]] =!= 3, 
    Message[Export::errelem, "VertexCoordinates", "XYZ"];
    Return[$Failed]];
   atoms = Graphics`MoleculePlotDump`ElementNamesToNumbers[atoms];
   If[! ArrayQ[atoms, 1, IntegerQ], 
    Message[Export::badval, "VertexTypes"]; Return[$Failed]];
   data = 
    Join[{{ToString[
        Length[atoms]]}}, \
{{System`ConvertersDump`Utilities`$signature}}, 
     Partition[
      Flatten[MapThread[
        List, {Graphics`MoleculePlotDump`NumbersToElementShortNames[
          atoms], Map[ToString[
            (* THIS IS WHAT HAS CHANGED *)
            PaddedForm[#1, {7, 6}]
            (* ------------------------ *)
            ] &, coords/100.`, {2}]}]], 4]];
   data = (If[Length[#1] === 1, First[#1], 
        StringJoin @@ Riffle[#1, "\t"]] &) /@ data;
   data = StringJoin @@ Riffle[data, "\n"];
   WriteString[strm, data]] /; 
  System`ConvertersDump`Utilities`ElementsQ[rules, 
   "VertexTypes" | "VertexCoordinates"]

myxyzexporter[filename_, rules_, opts___?OptionQ] := 
 Module[{noelems}, 
  If[Head[rules] =!= List || 
    MemberQ[
     MapThread[
      Or, {(MatchQ[#1, _ :> _] &) /@ rules, (MatchQ[#1, _ -> _] &) /@ 
        rules}], False], Message[Export::type, Head[rules], "XYZ"];
   Return[$Failed], 
   noelems = 
    DeleteCases[System`ConvertersDump`Utilities`ElementNames[rules], 
     "VertexTypes" | "VertexCoordinates"];
   Message[Export::noelem, noelems, "XYZ"]; Return[$Failed]]]  

The last step is to register the export function, which I'll call XYZ2

ImportExport`RegisterExport["XYZ2", myxyzexporter, "FunctionChannels" -> {"Streams"}]

Now let's fix your matrix, as it is missing some important quotation marks:

matrix = {"VertexTypes" -> {"C", "N", "C"}, 
  "VertexCoordinates" -> {{-0.447094, 0.754888, -1.66937}, {0.625759, 
     2.75335, -2.19647}, {1.76312, 0.724608, -2.34303}}}

Exporting with the new format gives us:

Export["fixed.xyz", matrix, "XYZ2"]
FilePrint["fixed.xyz"]

Mathematica graphics

whereas the old XYZ format gives us:

Export["normal.xyz", matrix]
FilePrint["normal.xyz"]

Mathematica graphics

Note: If you need to modify the number format further, you can refer to the PaddedForm documentation and alter the portion of code that is in between the comments.

$\endgroup$
4
  • $\begingroup$ This is VERY nice! I see that you maintained the unit conversion in your export function. I have not had much opportunity to work with chemical structures in Mathematica, but am I correct in assuming, then, that the molecular coordinates are internally expressed in picometers? I scanned the documentation, but couldn't find a definitive answer. Would you have a pointer? $\endgroup$
    – MarcoB
    May 12, 2015 at 4:24
  • $\begingroup$ @MarcoB I've looked through a number of XYZ files and there does not appear to be a set standard, although there is a preference for Angstroms over picometers. The font of all web knowledge seems to have it right this time. $\endgroup$ May 12, 2015 at 4:52
  • $\begingroup$ When trying your resolution as is, I received the error: Export::type: "RuleDelayed cannot be exported to the \!(\"XYZ\") format." $\endgroup$
    – Zoe
    May 13, 2015 at 4:53
  • $\begingroup$ @zoe I had a typo in the ImportExport line (forgot the first letter when cutting and pasting). Please try again and let me know if it works for you. $\endgroup$ May 13, 2015 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.