Package ovf2io
ovf2io contains utilities to read and write OOMMF Vector Field (.ovf) files.
Examples:
- Read an (.ovf) file.
file_dict = ovf2io.read_ovf("file.ovf")
print(file_dict['data']['m_x']) # ndarray of m_x values
print(file_dict['coords']['x']) # 1darray of x-coords
- Write an (.ovf) file, giving coordinate values.
import numpy as np
X = np.linspace(0, 1, 10)
x, y, z = np.meshgrid(X, X, X, indexing='ij') # indexing='ij' to make x=axis 0, y=axis 1
f = np.cos(x)[...,np.newaxis] # shape is (10, 10, 10, 1)
ovf2io.write_ovf_rectangular(f, "file.ovf", x=X, y=Y, z=Z)
- Write an (.ovf) file, giving initial point and cellsize.
import numpy as np
X = np.linspace(0, 1, 10)
x, y, z = np.meshgrid(X, X, X, indexing='ij') # indexing='ij' to make x=axis 0, y=axis 1
f = np.cos(x)[...,np.newaxis] # shape is (10, 10, 10, 1)
xmin, ymin, zmin = X[0], X[0], X[0]
dx = X[1]-X[0]
dy = X[1]-X[0]
dz = X[1]-X[0]
ovf2io.write_ovf_rectangular(f, "file.ovf", p0=(xmin, ymin, zmin), cellsize=(dx, dy, dz))
Expand source code
# ovf2io is a utility for OOMMF Vector Field (.ovf) IO developed by WSP as a member of the McMorran Lab
# Copyright (C) 2023 William S. Parker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""ovf2io contains utilities to read and write OOMMF Vector Field (.ovf) files.
Examples:
1. Read an (.ovf) file.
```python
file_dict = ovf2io.read_ovf("file.ovf")
print(file_dict['data']['m_x']) # ndarray of m_x values
print(file_dict['coords']['x']) # 1darray of x-coords
```
2. Write an (.ovf) file, giving coordinate values.
```python
import numpy as np
X = np.linspace(0, 1, 10)
x, y, z = np.meshgrid(X, X, X, indexing='ij') # indexing='ij' to make x=axis 0, y=axis 1
f = np.cos(x)[...,np.newaxis] # shape is (10, 10, 10, 1)
ovf2io.write_ovf_rectangular(f, "file.ovf", x=X, y=Y, z=Z)
```
3. Write an (.ovf) file, giving initial point and cellsize.
```python
import numpy as np
X = np.linspace(0, 1, 10)
x, y, z = np.meshgrid(X, X, X, indexing='ij') # indexing='ij' to make x=axis 0, y=axis 1
f = np.cos(x)[...,np.newaxis] # shape is (10, 10, 10, 1)
xmin, ymin, zmin = X[0], X[0], X[0]
dx = X[1]-X[0]
dy = X[1]-X[0]
dz = X[1]-X[0]
ovf2io.write_ovf_rectangular(f, "file.ovf", p0=(xmin, ymin, zmin), cellsize=(dx, dy, dz))
```
"""
from . import _utils as ut
import numpy as np
from pathlib import Path
from warnings import warn
__all__ = ["read_ovf",
"write_ovf_irregular",
"write_ovf_rectangular"]
def read_ovf(fname):
"""Returns a dictionary containing the information read from an .ovf file.
The returned dictionary has three items:
1. `'data'`, the actual values.
2. `'coords'`, generated coordinates based on xmin, xstepsize, and xnodes etc.
3. `'metadata'`, all of the information contained in the header.
`'data'` contains an entry for each axis of data stored, for example <br />
`read_ovf(fname)['data']['Zeeman energy density']` or <br />
`read_ovf(fname)['data']['m_x']`
`'coords'` contains coordinates generated from the header info, for example
`read_ovf(fname)['coords']['x']`
If meshtype is `'rectangular'`, the shape of each `'data'` entry and each `'coords'`
entry will be `(xnodes, ynodes, znodes)`. If meshtype is `'irregular'`, each
`'data'` and `'coords'` entry will be 1-dimensional.
**Parameters**
* **fname** : _str or Path_ <br />
The filename. <br />
**Returns**
* **file_dict** : _dict_ <br />
A dictionary containing the data, metadata, and generated coordinates.
"""
fname = Path(fname)
with open(fname, "rb") as f:
if not b"2.0" in next(f):
raise ValueError("This file does not appear to be OVF 2.0. "
"ovf2io does not support older OVF formats. ")
# Skip ahead to the header
while b"# begin: header" not in next(f).lower():
pass
header = ut._parse_header(f)
nbytes = ut._advance_to_data_block(f)
data = ut._parse_data(f, header, nbytes)
coords = ut._gen_coords(data, header)
header['repr'] = "text" if nbytes is None else f"Binary {nbytes}"
out = {
'data': data,
'coords': coords,
'metadata': header
}
return out
def write_ovf(data, fname, **kwargs):
"""Write data to an OOMMF Vector Field (.ovf) file.
Macro for `write_ovf_rectangular()` and `write_ovf_irregular`.
"""
warn("This function is deprecated, and is now essentially just a proxy for "
"`write_ovf_rectangular` and `write_ovf_rectangular`. "
"It is recommended to use either of those directly instead. ")
if len(data.shape) == 4:
write_ovf_rectangular(data, fname, **kwargs)
elif len(data.shape) == 2:
write_ovf_irregular(data, fname, **kwargs)
else:
raise Exception("Shape of data not recognized. Should be "
"(N_x, N_y, N_z, N_data_components) for rectangular meshes "
"or (N_points, N_data_components) for irregular meshes. ")
def write_ovf_rectangular(data, fname, p0=(0., 0., 0.,), cellsize=None,
x=None, y=None, z=None, title="title", desc=[], meshunit="m",
valueunits=[], valuelabels=[], representation="bin8",
):
"""Write data from a rectangular mesh to an OOMMF Vector Field (.ovf) file.
**Parameters**
* **data** : _ndarray_ <br />
Data should have shape `(N_x, N_y, N_z, N_data_components)`. For example,
a vector field with 3 components, 10 samples in the x-direction, 5 samples
in the y-direction, and 2 samples in the z-direction would have shape (10, 5, 2, 3).
* **fname** : _str or Path_ <br />
The name of the file to write. Will be overwritten if it exists already.
Intermediate directories are not created automatically.
* **p0** : _tuple, optional_ <br />
The coordinates of the first data point, in units of `meshunit`.
Note these are NOT the coordinates of the edge of the bounding box.
Ignored when **x**, **y**, and **z** are given. <br />
Default is `p0=(0., 0., 0.)`.
* **cellsize** : _tuple, optional_ <br />
The distance between adjacent grid points, in units of `meshunit`.
Ignored when **x**, **y**, and **z** are given. <br />
Default is `cellsize=(1., 1., 1.)`.
* **x** : _ndarray, optional_ <br />
X-coordinates, 1-dimensional array. Length must match the first axis of the data.
If not given, **p0** and **cellsize** will be used.
If given, **y** and **z** must also be given, and **p0** and **cellsize** will be ignored.
* **y** : _ndarray, optional_ <br />
Y-coordinates, 1-dimensional array. Length must match the second axis of the data.
If not given, **p0** and **cellsize** will be used.
If given, **x** and **z** must also be given, and **p0** and **cellsize** will be ignored.
* **z** : _ndarray, optional_ <br />
Z-coordinates, 1-dimensional array. Length must match the third axis of the data.
If not given, **p0** and **cellsize** will be used.
If given, **x** and **y** must also be given, and **p0** and **cellsize** will be ignored.
* **title** : _str, optional_ <br />
Title to be stored in metadata. <br />
Default is `title = "title"`.
* **desc** : _list, optional_ <br />
Multi-line description; each entry in the list is a separate line.
* **meshunit** : _str, optional_ <br />
The unit of x, y, and z coordinates. <br />
Default is `meshunit = "m"`.
* **valueunits** : _list, optional_ <br />
The units for each value stored. Length should match the number of data components,
or can be length 1 if all data components share the same units. If not given,
all data components will be given units "1".
* **valuelabels** : _list, optional_ <br />
Labels for each data component. Length should match the number of data components.
If not given, components will be labelled "value_0", "value_1", etc.
* **representation** : _str, optional_ <br />
The storage mode for the data itself. One of "text", "bin4", and "bin8".
Comments are allowed in text mode, which uses the UTF-8 encoding.
Note that the original specification specifies ASCII which is a subset of UTF-8. <br />
Default is `representation = "bin8"`.
"""
data = np.array(data)
if len(data.shape) != 4:
raise Exception("Data should have shape (N_x, N_y, N_z, N_data_components).")
# Add a line to desc saying generated by ovf2io
desc = ut._shape_desc(desc)
valuedim = data.shape[-1]
# Generate valueunits and valuelabels
valueunits = ut._generate_valueunits_list(valueunits, valuedim)
valuelabels = ut._generate_valuelabels_list(valuelabels, valuedim)
if x is not None and y is not None and z is not None:
if len(x) != data.shape[0] or len(y) != data.shape[1] or len(z) != data.shape[2]:
raise ValueError("Coordinate dimensions incorrect; lengths of x, y, and z "
"should match the data's first, second, and third axes, respectively. ")
p0 = (x[0], y[0], z[0])
cellsize = (np.abs(x[1]-x[0]), np.abs(y[1]-y[0]), np.abs(z[1]-z[0]))
elif x is not None or y is not None or z is not None:
raise Exception("x, y, and z should all be given or none given.")
# If no x/y/z, but also no cellsize
elif cellsize is None:
cellsize = (1., 1., 1.)
meshunit = "pt"
header = {
"title": title, "desc": desc, "meshunit": meshunit, "meshtype": "rectangular",
"valueunits": valueunits, "valuelabels": valuelabels, "valuedim": valuedim,
"xbase": p0[0], "ybase": p0[1], "zbase": p0[2],
"xstepsize": cellsize[0], "ystepsize": cellsize[1], "zstepsize": cellsize[2],
"xnodes": data.shape[0], "ynodes": data.shape[1], "znodes": data.shape[2],
"xmin": p0[0] - 0.5 * cellsize[0], "xmax": p0[0] + (data.shape[0] - 0.5) * cellsize[0],
"ymin": p0[1] - 0.5 * cellsize[1], "ymax": p0[1] + (data.shape[1] - 0.5) * cellsize[1],
"zmin": p0[2] - 0.5 * cellsize[2], "zmax": p0[2] + (data.shape[2] - 0.5) * cellsize[2]
}
if not representation.lower() in {"text", "bin4", "bin8"}:
raise ValueError("Representation must be either 'text', 'bin4', or 'bin8'.")
reshaped = data.reshape((-1, data.shape[-1]), order='F')
frontmatter = ut._make_header(header, representation)
ut._write_file(fname, frontmatter, representation, reshaped)
def write_ovf_irregular(data, fname, points=None, cellsize=(0., 0., 0.),
title="title", desc=[], meshunit="m",
valueunits=[], valuelabels=[], representation="bin8"
):
"""Write data from an irregular mesh to an OOMMF Vector Field (.ovf) file.
**Parameters**
* **data** : _ndarray_ <br />
The data to be written. Should have shape `(N_points, N_data_components)`.
* **fname** : _str or Path_ <br />
The name of the file to write. Will be overwritten if it exists already.
Intermediate directories are not created automatically.
* **points** : _ndarray, optional_ <br />
Coordinates of the mesh. Should have shape `(N_points, 3)`. If not given,
x will be used as an index for the data entries.
* **cellsize** : _tuple, optional_ <br />
Tuple specifying the spacing between grid points, which is used by some programs
as a display hint.
* **title** : _str, optional_ <br />
Title to be stored in metadata. <br />
Default is `title = "title"`.
* **desc** : _list, optional_ <br />
Multi-line description; each line should be a separate entry in the list.
* **meshunit** : _str, optional_ <br />
The unit of x, y, and z coordinates. <br />
Default is `meshunit = "m"`.
* **valueunits** : _list, optional_ <br />
The units for each value stored. Length should match the number of data components,
or can be length 1 if all data components share the same units. If not given,
all data components will be given units "1".
* **valuelabels** : _list, optional_ <br />
Labels for each data component. Length should match the number of data components.
If not given, components will be labelled "value_0", "value_1", etc.
* **representation** : _str, optional_ <br />
The storage mode for the data itself. One of "text", "bin4", and "bin8".
Comments are allowed in text mode, which uses the UTF-8 encoding.
Note that the original specification specifies ASCII which is a subset of UTF-8. <br />
Default is `representation = "bin8"`.
"""
data = np.array(data)
if len(data.shape) != 2:
raise Exception("Data should have shape (N_points, N_data_components).")
# Add a line to desc saying generated by ovf2io
desc = ut._shape_desc(desc)
valuedim = data.shape[-1]
valueunits = ut._generate_valueunits_list(valueunits, valuedim)
valuelabels = ut._generate_valuelabels_list(valuelabels, valuedim)
if points is None:
points = np.array([[i, 0., 0.] for i in range(data.shape[0])])
cellsize = (1., 1., 1.)
meshunit = "pt"
header = {
"title": title, "desc": desc, "meshunit": meshunit, "meshtype": "irregular",
"valueunits": valueunits, "valuelabels": valuelabels, "valuedim": valuedim,
"pointcount": data.shape[0],
"xmin": np.min(points[:,0]) - 0.5 * cellsize[0], "xmax": np.max(points[:,0]) + 0.5 * cellsize[0],
"ymin": np.min(points[:,1]) - 0.5 * cellsize[1], "ymax": np.max(points[:,1]) + 0.5 * cellsize[1],
"zmin": np.min(points[:,2]) - 0.5 * cellsize[2], "zmax": np.max(points[:,2]) + 0.5 * cellsize[2],
}
if not representation.lower() in {"text", "bin4", "bin8"}:
raise ValueError("Representation must be either 'text', 'bin4', or 'bin8'.")
reshaped = np.zeros((data.shape[0], data.shape[1] + 3))
reshaped[:, :3] = points
reshaped[:, 3:] = data
frontmatter = ut._make_header(header, representation)
ut._write_file(fname, frontmatter, representation, reshaped)
Functions
def read_ovf(fname)-
Returns a dictionary containing the information read from an .ovf file.
The returned dictionary has three items:
'data', the actual values.'coords', generated coordinates based on xmin, xstepsize, and xnodes etc.'metadata', all of the information contained in the header.
'data'contains an entry for each axis of data stored, for example
read_ovf(fname)['data']['Zeeman energy density']or
read_ovf(fname)['data']['m_x']'coords'contains coordinates generated from the header info, for exampleread_ovf(fname)['coords']['x']If meshtype is
'rectangular', the shape of each'data'entry and each'coords'entry will be(xnodes, ynodes, znodes). If meshtype is'irregular', each'data'and'coords'entry will be 1-dimensional.Parameters
- fname : str or Path
The filename.
Returns
- file_dict : dict
A dictionary containing the data, metadata, and generated coordinates.
Expand source code
def read_ovf(fname): """Returns a dictionary containing the information read from an .ovf file. The returned dictionary has three items: 1. `'data'`, the actual values. 2. `'coords'`, generated coordinates based on xmin, xstepsize, and xnodes etc. 3. `'metadata'`, all of the information contained in the header. `'data'` contains an entry for each axis of data stored, for example <br /> `read_ovf(fname)['data']['Zeeman energy density']` or <br /> `read_ovf(fname)['data']['m_x']` `'coords'` contains coordinates generated from the header info, for example `read_ovf(fname)['coords']['x']` If meshtype is `'rectangular'`, the shape of each `'data'` entry and each `'coords'` entry will be `(xnodes, ynodes, znodes)`. If meshtype is `'irregular'`, each `'data'` and `'coords'` entry will be 1-dimensional. **Parameters** * **fname** : _str or Path_ <br /> The filename. <br /> **Returns** * **file_dict** : _dict_ <br /> A dictionary containing the data, metadata, and generated coordinates. """ fname = Path(fname) with open(fname, "rb") as f: if not b"2.0" in next(f): raise ValueError("This file does not appear to be OVF 2.0. " "ovf2io does not support older OVF formats. ") # Skip ahead to the header while b"# begin: header" not in next(f).lower(): pass header = ut._parse_header(f) nbytes = ut._advance_to_data_block(f) data = ut._parse_data(f, header, nbytes) coords = ut._gen_coords(data, header) header['repr'] = "text" if nbytes is None else f"Binary {nbytes}" out = { 'data': data, 'coords': coords, 'metadata': header } return out def write_ovf_irregular(data, fname, points=None, cellsize=(0.0, 0.0, 0.0), title='title', desc=[], meshunit='m', valueunits=[], valuelabels=[], representation='bin8')-
Write data from an irregular mesh to an OOMMF Vector Field (.ovf) file.
Parameters
-
data : ndarray
The data to be written. Should have shape(N_points, N_data_components). -
fname : str or Path
The name of the file to write. Will be overwritten if it exists already. Intermediate directories are not created automatically. -
points : ndarray, optional
Coordinates of the mesh. Should have shape(N_points, 3). If not given, x will be used as an index for the data entries. -
cellsize : tuple, optional
Tuple specifying the spacing between grid points, which is used by some programs as a display hint. -
title : str, optional
Title to be stored in metadata.
Default istitle = "title". -
desc : list, optional
Multi-line description; each line should be a separate entry in the list. -
meshunit : str, optional
The unit of x, y, and z coordinates.
Default ismeshunit = "m". -
valueunits : list, optional
The units for each value stored. Length should match the number of data components, or can be length 1 if all data components share the same units. If not given, all data components will be given units "1". -
valuelabels : list, optional
Labels for each data component. Length should match the number of data components. If not given, components will be labelled "value_0", "value_1", etc. -
representation : str, optional
The storage mode for the data itself. One of "text", "bin4", and "bin8". Comments are allowed in text mode, which uses the UTF-8 encoding. Note that the original specification specifies ASCII which is a subset of UTF-8.
Default isrepresentation = "bin8".
Expand source code
def write_ovf_irregular(data, fname, points=None, cellsize=(0., 0., 0.), title="title", desc=[], meshunit="m", valueunits=[], valuelabels=[], representation="bin8" ): """Write data from an irregular mesh to an OOMMF Vector Field (.ovf) file. **Parameters** * **data** : _ndarray_ <br /> The data to be written. Should have shape `(N_points, N_data_components)`. * **fname** : _str or Path_ <br /> The name of the file to write. Will be overwritten if it exists already. Intermediate directories are not created automatically. * **points** : _ndarray, optional_ <br /> Coordinates of the mesh. Should have shape `(N_points, 3)`. If not given, x will be used as an index for the data entries. * **cellsize** : _tuple, optional_ <br /> Tuple specifying the spacing between grid points, which is used by some programs as a display hint. * **title** : _str, optional_ <br /> Title to be stored in metadata. <br /> Default is `title = "title"`. * **desc** : _list, optional_ <br /> Multi-line description; each line should be a separate entry in the list. * **meshunit** : _str, optional_ <br /> The unit of x, y, and z coordinates. <br /> Default is `meshunit = "m"`. * **valueunits** : _list, optional_ <br /> The units for each value stored. Length should match the number of data components, or can be length 1 if all data components share the same units. If not given, all data components will be given units "1". * **valuelabels** : _list, optional_ <br /> Labels for each data component. Length should match the number of data components. If not given, components will be labelled "value_0", "value_1", etc. * **representation** : _str, optional_ <br /> The storage mode for the data itself. One of "text", "bin4", and "bin8". Comments are allowed in text mode, which uses the UTF-8 encoding. Note that the original specification specifies ASCII which is a subset of UTF-8. <br /> Default is `representation = "bin8"`. """ data = np.array(data) if len(data.shape) != 2: raise Exception("Data should have shape (N_points, N_data_components).") # Add a line to desc saying generated by ovf2io desc = ut._shape_desc(desc) valuedim = data.shape[-1] valueunits = ut._generate_valueunits_list(valueunits, valuedim) valuelabels = ut._generate_valuelabels_list(valuelabels, valuedim) if points is None: points = np.array([[i, 0., 0.] for i in range(data.shape[0])]) cellsize = (1., 1., 1.) meshunit = "pt" header = { "title": title, "desc": desc, "meshunit": meshunit, "meshtype": "irregular", "valueunits": valueunits, "valuelabels": valuelabels, "valuedim": valuedim, "pointcount": data.shape[0], "xmin": np.min(points[:,0]) - 0.5 * cellsize[0], "xmax": np.max(points[:,0]) + 0.5 * cellsize[0], "ymin": np.min(points[:,1]) - 0.5 * cellsize[1], "ymax": np.max(points[:,1]) + 0.5 * cellsize[1], "zmin": np.min(points[:,2]) - 0.5 * cellsize[2], "zmax": np.max(points[:,2]) + 0.5 * cellsize[2], } if not representation.lower() in {"text", "bin4", "bin8"}: raise ValueError("Representation must be either 'text', 'bin4', or 'bin8'.") reshaped = np.zeros((data.shape[0], data.shape[1] + 3)) reshaped[:, :3] = points reshaped[:, 3:] = data frontmatter = ut._make_header(header, representation) ut._write_file(fname, frontmatter, representation, reshaped) -
def write_ovf_rectangular(data, fname, p0=(0.0, 0.0, 0.0), cellsize=None, x=None, y=None, z=None, title='title', desc=[], meshunit='m', valueunits=[], valuelabels=[], representation='bin8')-
Write data from a rectangular mesh to an OOMMF Vector Field (.ovf) file.
Parameters
-
data : ndarray
Data should have shape(N_x, N_y, N_z, N_data_components). For example, a vector field with 3 components, 10 samples in the x-direction, 5 samples in the y-direction, and 2 samples in the z-direction would have shape (10, 5, 2, 3). -
fname : str or Path
The name of the file to write. Will be overwritten if it exists already. Intermediate directories are not created automatically. -
p0 : tuple, optional
The coordinates of the first data point, in units ofmeshunit. Note these are NOT the coordinates of the edge of the bounding box. Ignored when x, y, and z are given.
Default isp0=(0., 0., 0.). -
cellsize : tuple, optional
The distance between adjacent grid points, in units ofmeshunit. Ignored when x, y, and z are given.
Default iscellsize=(1., 1., 1.). -
x : ndarray, optional
X-coordinates, 1-dimensional array. Length must match the first axis of the data. If not given, p0 and cellsize will be used. If given, y and z must also be given, and p0 and cellsize will be ignored. -
y : ndarray, optional
Y-coordinates, 1-dimensional array. Length must match the second axis of the data. If not given, p0 and cellsize will be used. If given, x and z must also be given, and p0 and cellsize will be ignored. -
z : ndarray, optional
Z-coordinates, 1-dimensional array. Length must match the third axis of the data. If not given, p0 and cellsize will be used. If given, x and y must also be given, and p0 and cellsize will be ignored. -
title : str, optional
Title to be stored in metadata.
Default istitle = "title". -
desc : list, optional
Multi-line description; each entry in the list is a separate line. -
meshunit : str, optional
The unit of x, y, and z coordinates.
Default ismeshunit = "m". -
valueunits : list, optional
The units for each value stored. Length should match the number of data components, or can be length 1 if all data components share the same units. If not given, all data components will be given units "1". -
valuelabels : list, optional
Labels for each data component. Length should match the number of data components. If not given, components will be labelled "value_0", "value_1", etc. -
representation : str, optional
The storage mode for the data itself. One of "text", "bin4", and "bin8". Comments are allowed in text mode, which uses the UTF-8 encoding. Note that the original specification specifies ASCII which is a subset of UTF-8.
Default isrepresentation = "bin8".
Expand source code
def write_ovf_rectangular(data, fname, p0=(0., 0., 0.,), cellsize=None, x=None, y=None, z=None, title="title", desc=[], meshunit="m", valueunits=[], valuelabels=[], representation="bin8", ): """Write data from a rectangular mesh to an OOMMF Vector Field (.ovf) file. **Parameters** * **data** : _ndarray_ <br /> Data should have shape `(N_x, N_y, N_z, N_data_components)`. For example, a vector field with 3 components, 10 samples in the x-direction, 5 samples in the y-direction, and 2 samples in the z-direction would have shape (10, 5, 2, 3). * **fname** : _str or Path_ <br /> The name of the file to write. Will be overwritten if it exists already. Intermediate directories are not created automatically. * **p0** : _tuple, optional_ <br /> The coordinates of the first data point, in units of `meshunit`. Note these are NOT the coordinates of the edge of the bounding box. Ignored when **x**, **y**, and **z** are given. <br /> Default is `p0=(0., 0., 0.)`. * **cellsize** : _tuple, optional_ <br /> The distance between adjacent grid points, in units of `meshunit`. Ignored when **x**, **y**, and **z** are given. <br /> Default is `cellsize=(1., 1., 1.)`. * **x** : _ndarray, optional_ <br /> X-coordinates, 1-dimensional array. Length must match the first axis of the data. If not given, **p0** and **cellsize** will be used. If given, **y** and **z** must also be given, and **p0** and **cellsize** will be ignored. * **y** : _ndarray, optional_ <br /> Y-coordinates, 1-dimensional array. Length must match the second axis of the data. If not given, **p0** and **cellsize** will be used. If given, **x** and **z** must also be given, and **p0** and **cellsize** will be ignored. * **z** : _ndarray, optional_ <br /> Z-coordinates, 1-dimensional array. Length must match the third axis of the data. If not given, **p0** and **cellsize** will be used. If given, **x** and **y** must also be given, and **p0** and **cellsize** will be ignored. * **title** : _str, optional_ <br /> Title to be stored in metadata. <br /> Default is `title = "title"`. * **desc** : _list, optional_ <br /> Multi-line description; each entry in the list is a separate line. * **meshunit** : _str, optional_ <br /> The unit of x, y, and z coordinates. <br /> Default is `meshunit = "m"`. * **valueunits** : _list, optional_ <br /> The units for each value stored. Length should match the number of data components, or can be length 1 if all data components share the same units. If not given, all data components will be given units "1". * **valuelabels** : _list, optional_ <br /> Labels for each data component. Length should match the number of data components. If not given, components will be labelled "value_0", "value_1", etc. * **representation** : _str, optional_ <br /> The storage mode for the data itself. One of "text", "bin4", and "bin8". Comments are allowed in text mode, which uses the UTF-8 encoding. Note that the original specification specifies ASCII which is a subset of UTF-8. <br /> Default is `representation = "bin8"`. """ data = np.array(data) if len(data.shape) != 4: raise Exception("Data should have shape (N_x, N_y, N_z, N_data_components).") # Add a line to desc saying generated by ovf2io desc = ut._shape_desc(desc) valuedim = data.shape[-1] # Generate valueunits and valuelabels valueunits = ut._generate_valueunits_list(valueunits, valuedim) valuelabels = ut._generate_valuelabels_list(valuelabels, valuedim) if x is not None and y is not None and z is not None: if len(x) != data.shape[0] or len(y) != data.shape[1] or len(z) != data.shape[2]: raise ValueError("Coordinate dimensions incorrect; lengths of x, y, and z " "should match the data's first, second, and third axes, respectively. ") p0 = (x[0], y[0], z[0]) cellsize = (np.abs(x[1]-x[0]), np.abs(y[1]-y[0]), np.abs(z[1]-z[0])) elif x is not None or y is not None or z is not None: raise Exception("x, y, and z should all be given or none given.") # If no x/y/z, but also no cellsize elif cellsize is None: cellsize = (1., 1., 1.) meshunit = "pt" header = { "title": title, "desc": desc, "meshunit": meshunit, "meshtype": "rectangular", "valueunits": valueunits, "valuelabels": valuelabels, "valuedim": valuedim, "xbase": p0[0], "ybase": p0[1], "zbase": p0[2], "xstepsize": cellsize[0], "ystepsize": cellsize[1], "zstepsize": cellsize[2], "xnodes": data.shape[0], "ynodes": data.shape[1], "znodes": data.shape[2], "xmin": p0[0] - 0.5 * cellsize[0], "xmax": p0[0] + (data.shape[0] - 0.5) * cellsize[0], "ymin": p0[1] - 0.5 * cellsize[1], "ymax": p0[1] + (data.shape[1] - 0.5) * cellsize[1], "zmin": p0[2] - 0.5 * cellsize[2], "zmax": p0[2] + (data.shape[2] - 0.5) * cellsize[2] } if not representation.lower() in {"text", "bin4", "bin8"}: raise ValueError("Representation must be either 'text', 'bin4', or 'bin8'.") reshaped = data.reshape((-1, data.shape[-1]), order='F') frontmatter = ut._make_header(header, representation) ut._write_file(fname, frontmatter, representation, reshaped) -