From 60998dd10182458fa686046f12ea10eaa09a697e Mon Sep 17 00:00:00 2001 From: Giacomo Fiorin Date: Mon, 12 Nov 2018 14:00:00 -0500 Subject: [PATCH 1/4] Add typequote argument to control whether DX array type should be quoted This change works around another example of poorly hard-coded DX parser (see also issue #35). See the code below from the GridForcesGrid.C file in NAMD: fscanf(poten_fp, "object %*d class array type double rank 0 " "items %*d data follows\n"); which will only work if the quote character (introduced in 115b05af to appease the PyMol parser) is omitted. --- gridData/OpenDX.py | 9 ++++++--- gridData/core.py | 20 +++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/gridData/OpenDX.py b/gridData/OpenDX.py index da18ec6..3d8c1d7 100644 --- a/gridData/OpenDX.py +++ b/gridData/OpenDX.py @@ -301,7 +301,8 @@ class array(DXclass): # "string" not automatically supported } - def __init__(self, classid, array=None, type=None, **kwargs): + def __init__(self, classid, array=None, type=None, typequote='"', + **kwargs): """ Parameters ---------- @@ -347,6 +348,7 @@ def __init__(self, classid, array=None, type=None, **kwargs): "types are: {1}".format(type, list(self.dx_types.values())))) self.type = type + self.typequote = typequote def write(self, file): """Write the *class array* section. @@ -367,9 +369,10 @@ def write(self, file): "Supported valus are: {}\n" "Use the type= keyword argument.").format( self.type, list(self.dx_types.keys()))) + typelabel = (self.typequote+self.type+self.typequote) DXclass.write(self,file, - 'type "{0}" rank 0 items {1} data follows'.format( - self.type, self.array.size)) + 'type {0} rank 0 items {1} data follows'.format( + typelabel, self.array.size)) # grid data, serialized as a C array (z fastest varying) # (flat iterator is equivalent to: for x: for y: for z: grid[x,y,z]) # VMD's DX reader requires exactly 3 values per line diff --git a/gridData/core.py b/gridData/core.py index 01276bd..30a5dd9 100644 --- a/gridData/core.py +++ b/gridData/core.py @@ -405,7 +405,7 @@ def _load_plt(self, filename): grid, edges = g.histogramdd() self.__init__(grid=grid, edges=edges, metadata=self.metadata) - def export(self, filename, file_format=None, type=None): + def export(self, filename, file_format=None, type=None, typequote='"'): """export density to file using the given format. The format can also be deduced from the suffix of the filename @@ -424,10 +424,13 @@ def export(self, filename, file_format=None, type=None): Parameters ---------- + filename : str name of the output file + file_format : {'dx', 'pickle', None} (optional) output file format, the default is "dx" + type : str (optional) for DX, set the output DX array type, e.g., "double" or "float"; note that PyMOL only understands "double" (see @@ -437,12 +440,19 @@ def export(self, filename, file_format=None, type=None): .. versionadded:: 0.4.0 - .. _`#35`: https://github.com/MDAnalysis/GridDataFormats/issues/35 + typequote : str (optional) + For DX, set the character used to quote the type string; + by default this is a double-quote character, '"'. + Custom parsers like the one from NAMD-GridForces (backend for MDFF) + expect no quotes, and typequote='' may be used to appease them. + + .. versionadded:: after 0.4.0 + """ exporter = self._get_exporter(filename, file_format=file_format) - exporter(filename, type=type) + exporter(filename, type=type, typequote=typequote) # note: the _export_FORMAT() methods all take the filename as a mandatory # argument. They can process kwargs but they are not required to do @@ -461,7 +471,7 @@ def _export_python(self, filename, **kwargs): with open(filename, 'wb') as f: cPickle.dump(data, f, cPickle.HIGHEST_PROTOCOL) - def _export_dx(self, filename, type=None, **kwargs): + def _export_dx(self, filename, type=None, typequote='"', **kwargs): """Export the density grid to an OpenDX file. The file format is the simplest regular grid array and it is @@ -495,7 +505,7 @@ def _export_dx(self, filename, type=None, **kwargs): positions=OpenDX.gridpositions(1, self.grid.shape, self.origin, self.delta), connections=OpenDX.gridconnections(2, self.grid.shape), - data=OpenDX.array(3, self.grid, type=type), + data=OpenDX.array(3, self.grid, type=type, typequote=typequote), ) dx = OpenDX.field('density', components=components, comments=comments) dx.write(filename) From 6154342c0ab51fd15747c9df4db042d8552c10ff Mon Sep 17 00:00:00 2001 From: Giacomo Fiorin Date: Mon, 12 Nov 2018 14:48:32 -0500 Subject: [PATCH 2/4] Use (fixed-point) precision of NumPy array when writing DX file Again, this is a workaround for an issue in the PyMol DX parser that fails to parse tiny numbers (i.e. with an exponent lower than -100). If it seems inconsistent with requiring "type double", it is because the DX parser uses a "float" to read the data: static int read_dx_data(void *v, int set, float *datablock, float *colorblock) { ... float grid; ... Observed with PyMol up to 2.3.0. --- gridData/OpenDX.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gridData/OpenDX.py b/gridData/OpenDX.py index 3d8c1d7..2390fa5 100644 --- a/gridData/OpenDX.py +++ b/gridData/OpenDX.py @@ -376,12 +376,16 @@ def write(self, file): # grid data, serialized as a C array (z fastest varying) # (flat iterator is equivalent to: for x: for y: for z: grid[x,y,z]) # VMD's DX reader requires exactly 3 values per line + fmt_string = "{:d}" + if (self.array.dtype.kind == 'f' or self.array.dtype.kind == 'c'): + precision = numpy.finfo(self.array.dtype).precision + fmt_string = "{:."+"{:d}".format(precision)+"f}" values_per_line = 3 values = self.array.flat while 1: try: for i in range(values_per_line): - file.write(str(next(values)) + "\t") + file.write(fmt_string.format(next(values)) + "\t") file.write('\n') except StopIteration: file.write('\n') From cbb8247c4d3f377bb0b49618221a15b8bc59bc89 Mon Sep 17 00:00:00 2001 From: Giacomo Fiorin Date: Mon, 8 Apr 2019 11:11:34 -0400 Subject: [PATCH 3/4] Fix version tag for typequote keyword --- gridData/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridData/core.py b/gridData/core.py index 6d86e7f..8b2aa36 100644 --- a/gridData/core.py +++ b/gridData/core.py @@ -450,7 +450,7 @@ def export(self, filename, file_format=None, type=None, typequote='"'): Custom parsers like the one from NAMD-GridForces (backend for MDFF) expect no quotes, and typequote='' may be used to appease them. - .. versionadded:: after 0.5.0 + .. versionadded:: 0.5.0 """ exporter = self._get_exporter(filename, file_format=file_format) From 63441c0b2a31b043090c7ee7f10d111207767ee3 Mon Sep 17 00:00:00 2001 From: Giacomo Fiorin Date: Mon, 8 Apr 2019 11:21:37 -0400 Subject: [PATCH 4/4] Update DX type keyword doc to reflect changes in Pymol --- gridData/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gridData/core.py b/gridData/core.py index 8b2aa36..5be5819 100644 --- a/gridData/core.py +++ b/gridData/core.py @@ -434,11 +434,10 @@ def export(self, filename, file_format=None, type=None, typequote='"'): output file format, the default is "dx" type : str (optional) - for DX, set the output DX array type, e.g., "double" or - "float"; note that PyMOL only understands "double" (see - issue `#35`_). By default (``None``), the DX type is - determined from the numpy dtype of the array of the grid - (and this will typically result in "double"). + for DX, set the output DX array type, e.g., "double" or "float". + By default (``None``), the DX type is determined from the numpy + dtype of the array of the grid (and this will typically result in + "double"). .. versionadded:: 0.4.0