Writing mesh files¶
To write a 2DM mesh file, instantiate a py2dm.Writer
object.
As with py2dm.Reader
, the preferred way to do this is via a context manager. This ensures the file is written and closed properly after writing.
Basic usage¶
You can add geometries to the mesh using the py2dm.Writer.element()
, py2dm.Writer.node()
, and py2dm.Writer.node_string()
methods.
These methods support two modes. In the first, the target object is instantiated first and passed to the method as a finished instance:
with py2dm.Writer('path/to/mesh.2dm') as mesh:
my_node = py2dm.Node(1, 25.0, 20.0, 5.0)
...
mesh.node(my_nodes)
This is the recommended mode when using Py2DM objects as your main data type, such as when copying entities across files or when merging two files.
Note
Any mutable lists are deep copied when passing a class instance to any of these methods. There is no danger of unintentional mutation of an object’s data after it was passed to this method.
Alternatively, all the above methods also support a factory interface, where the values passed are forwarded directly to the corresponding object constructor:
with py2dm.Writer('path/to/mesh.2dm') as mesh:
mesh.node(1, 25.0, 20.0, 5.0)
When creating elements via the factory pattern, you must also specify the class or 2DM card of the element type to create:
with py2dm.Writer('path/to/mesh.2dm') as mesh:
mesh.element('E3T', 1, 1, 2, 3)
# Or, alternatively:
mesh.element(py2dm.Element3T, 2, 3, 2, 4)
Automatically assigned IDs¶
The py2dm.Writer.node()
and py2dm.Writer.element()
methods both allow passing negative integers as IDs, in which case the ID will be determined based on the number of existing nodes/elements in the mesh. The assigned ID is then returned:
with py2dm.Writer('path/to/mesh.2dm') as mesh:
mesh.node(-1, -1.0, 1.0, 0.0) # returns 1
mesh.node(-1, 1.0, -1.0, 0.0) # returns 2
mesh.node(-1, 1.0, 1.0, 0.0) # returns 3
mesh.element('E3T', -1, 1, 2, 3) # returns 1
Writing chunks¶
The py2dm.Writer.element()
, py2dm.Writer.node()
, and py2dm.Writer.node_string()
methods do not commit any changes to disk. Instead, they write the elements to an internal cache that is later written to disk in a single chunk.
When the py2dm.Writer
class’s context manager is existed, this is done automatically. Until then, the entire node and element list is cached in memory.
For small meshes (<100’000 elements), this is not a problem. However, for larger meshes it may be preferable to write the file in chunks of a few thousand entities (i.e. rows) at a time.
To do this, the py2dm.Writer
class provides the py2dm.Writer.flush_elements()
, py2dm.Writer.flush_nodes()
, and py2dm.Writer.flush_node_strings()
methods respectively, which allow committing the cache to disk, freeing up its memory:
import random
import py2dm
def random_point(scale):
return tuple((random.random()*scale for _ in range(3)))
with py2dm.Reader('path/to/mesh.2dm') as mesh:
for i in range(1_000_000):
mesh.node(-1, *random_point())
if i % 10_000 == 0:
mesh.flush_nodes()
...
This is particularly useful when converting large files from other formats, since both the read and write operations can be done without loading either file into memory completely.
Important
When writing files in chunks, it is not possible to intermix entity types. This is due to the node and element ID ranges needing to be consecutive blocks in the file.
If you already committed nodes to a file and write any elements or node strings, you can no longer add any new nodes without an error being raised (and vice-versa).
Writer class interface¶
-
class
py2dm.
Writer
¶ Py2DM writer class used to validate and write 2DM files.
Any elements are initially written to memory and only get committed to disk once the writer is closed or one of the
flush_*()
methods is called.-
__init__
(filepath: str, **kwargs) → None¶ Create a new mesh writer.
- Parameters
filepath (
str
|pathlib.Path
) – Path to the mesh file to write.encoding (
str
) – Text encoding to use for the file.
-
name
: str¶ Display name of the mesh.
A custom name to store in the mesh to aid with identification. Can be written to disk as part of a
write_header()
call.- Type
-
property
closed
¶ Return whether the underlying file is closed.
After closing (either via the
close()
method or by leaving the reader’s context manager), any operations requiring use of the underlying file will raise apy2dm.errors.FileIsClosedError
.- Type
-
property
materials_per_element
¶ Number of materials per element.
This value can be specified via the materials keyword as part of the
Writer
class’s initialiser, or it will be inferred from the first element passed. Whenwrite_header()
is called with no elements added, the number of elements is additionally initialised to zero.Once set, this value can not be modified. Errors will be raised for elements with fewer materials than required, and extraneous materials will be stripped as part of the
element()
method.When writing the mesh to disk, this value will be stored in the
NUM_MATERIALS_PER_ELEM <count>
card at the top of the file.- Type
-
close
() → None¶ Close the mesh reader.
This commits any pending data to disk and closes the underlying text file. The instance will become unusable after this call.
Note
This method is called automatically when using the class via the context manager.
-
open
() → None¶ Open the mesh reader.
This performs the initial metadata read and sets up the class for continued access.
When calling this function manually, be sure to call
close()
once you no longer require file access. Alternatively, you can use the context manager interface, in which case both methods will be called automatically.
-
element
(element: py2dm.Element) → int¶
-
element
(element: type[py2dm.Element] | str, id_: int, *nodes: int, materials: tuple[int | float, …] = None) → int Add an element to the mesh.
If element is an instance of a
Element
subclass, a deep copy will be created. In this case, no other arguments may be passed.Alternatively, this method can be used as a factory, with the element field being either the subclass to instantiate or the name of the 2DM card of the element. When using the factory, any extra arguments are passed on to the class’s initialiser. Refer to the docstring of the respective class for supported arguments.
If the ID of the element is negative, one will be selected automatically based on the number of existing elements.
- Parameters
element (
py2dm.Element
|type
[py2dm.Element
] |str
]) – The instance or type of element to add.*args (
typing.Any
) – Extra arguments to forward to the class’s initialiser.**kwargs (
typing.Any
) – Extra keyword arguments to forward to the class’s initialiser.
- Raises
TypeError – Raised when extra arguments are passed while also passing a
py2dm.Element
instance.- Returns
The ID of the element that was added.
- Return type
-
node
(node: py2dm.Node) → int¶
-
node
(node: int, x: float, y: float, z: float) → int Add a node to the mesh.
If node is an instance of
Node
, a deep copy will be created. In this case, no other arguments may be passed.Alternatively, this method can be used as a factory, with the node field being used for the ID of the node to create. When using the factory, any extra arguments are passed on to the
py2dm.Node
initialiser. Refer to its docstring for supported arguments.If the ID of the node is negative, one will be selected automatically based on the number of existing nodes.
- Parameters
node (
py2dm.Node
|int
) – The node instance to add, or the ID of the node to create.*args (
typing.Any
) – Extra arguments to forward to thepy2dm.Node
initialiser.**kwargs (
typing.Any
) – Extra keyword arguments to forward to thepy2dm.Node
initialiser.
- Raises
TypeError – Raised when extra arguments are passed while also passing a
py2dm.Node
instance.- Returns
The ID of the node that was added.
- Return type
-
node_string
(node_string: py2dm.NodeString) → int¶
-
node_string
(node_string: int, *nodes: int, name: Union[str, None] = None) → int Add a node string to the mesh.
If node_string is an instance of
NodeString
, a deep copy will be created. In this case, no other arguments may be passed.Alternatively, this method can be used as a factory, with the node_string field being used for the first node ID in the string. When using the factory, any extra arguments are passed on to the
py2dm.NodeString
initialiser. Refer to its docstring for supported arguments.- Parameters
node_string – The node string instance to add, or the ID of the first node for the node string to create.
*args (
typing.Any
) – Extra arguments to forward to thepy2dm.NodeString
initialiser.**kwargs (
typing.Any
) – Extra keyword arguments to forward to thepy2dm.NodeString
initialiser.
- Raises
TypeError – Raised when extra arguments are passed while also passing a
py2dm.NodeString
instance.- Returns
The zero-based index of the node string in the mesh’s list of node strings.
- Return type
-
flush_elements
(**kwargs) → None¶ Write the local element cache to disk.
This clears out the in-memory element cache and writes its contents to disk. This method is called automatically when the
Writer
is closed.
-
flush_nodes
(**kwargs) → None¶ Write the local node cache to disk.
This clears out the in-memory node cache and writes its contents to disk. This method is called automatically when the
Writer
is closed.
-
flush_node_strings
(**kwargs) → None¶ Write the local node string cache to disk.
This clears out the in-memory node string cache and writes its contents to disk. This method is called automatically when the
Writer
is closed.
-
write_header
(signature: str = '') → None¶ Write the header of the 2DM file.
This writes the initial
MESH2D
format identifier, as well as theNUM_MATERIALS_PER_ELEM
field.The optional signature argument allows specifying a custom string to append to the initial line in the form of a comment. This string may contain newlines.
- Parameters
signature (
str
) – An authoring signature to include.
-