pyuff 2.5.8__tar.gz → 2.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyuff
3
- Version: 2.5.8
3
+ Version: 2.6.0
4
4
  Summary: UFF (Universal File Format) read/write.
5
5
  Project-URL: homepage, https://github.com/ladisk/pyuff
6
6
  Project-URL: documentation, https://pyuff.readthedocs.io/en/latest/
@@ -14,12 +14,11 @@ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
17
  Classifier: Programming Language :: Python :: 3.13
20
18
  Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Programming Language :: Python :: 3.15
21
20
  Classifier: Topic :: Scientific/Engineering
22
- Requires-Python: >=3.11
21
+ Requires-Python: >=3.13
23
22
  Requires-Dist: numpy
24
23
  Provides-Extra: dev
25
24
  Requires-Dist: build; extra == 'dev'
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pyuff"
7
- version = "2.5.8"
7
+ version = "2.6.0"
8
8
  authors = [{name = "Primož Čermelj, Janko Slavič", email = "janko.slavic@fs.uni-lj.si"}]
9
9
  maintainers = [{name = "Janko Slavič et al.", email = "janko.slavic@fs.uni-lj.si"}]
10
10
  license = "MIT"
11
11
  description = "UFF (Universal File Format) read/write."
12
12
  readme = "README.rst"
13
13
  keywords = ['UFF', 'UNV', 'Universal File Format', 'read/write']
14
- requires-python = ">=3.11"
14
+ requires-python = ">=3.13"
15
15
  dependencies = [
16
16
  "numpy",
17
17
  ]
@@ -20,10 +20,9 @@ classifiers = [
20
20
  'Intended Audience :: Developers',
21
21
  'Topic :: Scientific/Engineering',
22
22
  'Programming Language :: Python :: 3',
23
- 'Programming Language :: Python :: 3.11',
24
- 'Programming Language :: Python :: 3.12',
25
23
  'Programming Language :: Python :: 3.13',
26
24
  'Programming Language :: Python :: 3.14',
25
+ 'Programming Language :: Python :: 3.15',
27
26
  "License :: OSI Approved :: MIT License",
28
27
  ]
29
28
 
@@ -2,7 +2,7 @@ import numpy as np
2
2
  import time
3
3
  import os
4
4
 
5
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
5
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
6
6
 
7
7
  def get_structure_151(raw=False):
8
8
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -61,15 +61,18 @@ def _write151(fh, dset):
61
61
  'time_file_written': ts})
62
62
  # write strings to the file
63
63
  fh.write('%6i\n%6i%74s\n' % (-1, 151, ' '))
64
- fh.write('%-80s\n' % dset['model_name'])
65
- fh.write('%-80s\n' % dset['description'])
66
- fh.write('%-80s\n' % dset['db_app'])
67
- fh.write('%-10s%-10s%10s%10s%10s\n' % (dset['date_db_created'],
68
- dset['time_db_created'], dset['version_db1'], dset['version_db2'],
64
+ fh.write('%-80s\n' % _fit_string(dset['model_name'], 80, 'model_name', 151))
65
+ fh.write('%-80s\n' % _fit_string(dset['description'], 80, 'description', 151))
66
+ fh.write('%-80s\n' % _fit_string(dset['db_app'], 80, 'db_app', 151))
67
+ fh.write('%-10s%-10s%10s%10s%10s\n' % (_fit_string(dset['date_db_created'], 10, 'date_db_created', 151),
68
+ _fit_string(dset['time_db_created'], 10, 'time_db_created', 151),
69
+ dset['version_db1'], dset['version_db2'],
69
70
  dset['file_type']))
70
- fh.write('%-10s%-10s\n' % (dset['date_db_saved'], dset['time_db_saved']))
71
- fh.write('%-80s\n' % dset['program'])
72
- fh.write('%-10s%-10s\n' % (dset['date_file_written'], dset['time_file_written']))
71
+ fh.write('%-10s%-10s\n' % (_fit_string(dset['date_db_saved'], 10, 'date_db_saved', 151),
72
+ _fit_string(dset['time_db_saved'], 10, 'time_db_saved', 151)))
73
+ fh.write('%-80s\n' % _fit_string(dset['program'], 80, 'program', 151))
74
+ fh.write('%-10s%-10s\n' % (_fit_string(dset['date_file_written'], 10, 'date_file_written', 151),
75
+ _fit_string(dset['time_file_written'], 10, 'time_file_written', 151)))
73
76
  fh.write('%6i\n' % -1)
74
77
  except KeyError as msg:
75
78
  raise Exception('The required key \'' + msg.args[0] + '\' not present when writing data-set #151')
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import os
3
3
 
4
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
4
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
5
5
 
6
6
  def get_structure_164(raw=False):
7
7
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -52,7 +52,9 @@ def _write164(fh, dset):
52
52
  'temp_mode': 1})
53
53
  # write strings to the file
54
54
  fh.write('%6i\n%6i%74s\n' % (-1, 164, ' '))
55
- fh.write('%10i%20s%10i\n' % (dset['units_code'], dset['units_description'], dset['temp_mode']))
55
+ fh.write('%10i%20s%10i\n' % (dset['units_code'],
56
+ _fit_string(dset['units_description'], 20, 'units_description', 164),
57
+ dset['temp_mode']))
56
58
  str = '%25.16e%25.16e%25.16e\n%25.16e\n' % (
57
59
  dset['length'], dset['force'], dset['temp'], dset['temp_offset'])
58
60
  str = str.replace('e+', 'D+')
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
3
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
4
4
 
5
5
 
6
6
  def get_structure_18(raw=False):
@@ -60,7 +60,7 @@ def _write18(fh, dset):
60
60
  int(dset['ref_cs_num'][i]),
61
61
  int(dset['color'][i]),
62
62
  int(dset['method'][i])))
63
- fh.write('%-40s\n' % dset['cs_name'][i])
63
+ fh.write('%-40s\n' % _fit_string(dset['cs_name'][i], 40, 'cs_name', 18))
64
64
  fh.write('%13.5e%13.5e%13.5e%13.5e%13.5e%13.5e\n' % (
65
65
  dset['ref_o'][i][0], dset['ref_o'][i][1], dset['ref_o'][i][2],
66
66
  dset['x_point'][i][0], dset['x_point'][i][1], dset['x_point'][i][2]))
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
3
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
4
4
 
5
5
  def get_structure_1858(raw=False):
6
6
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -155,9 +155,9 @@ def _write1858(fh, dset):
155
155
  fh.write('%15.7e%15.7e%15.7e%15.7e%15.7e\n' % (0, 0, 0, 0, 0))
156
156
 
157
157
  fh.write('%-4s%2s%-4s\n' % (
158
- dset['response_direction'],
158
+ _fit_string(dset['response_direction'], 4, 'response_direction', 1858),
159
159
  '',
160
- dset['reference_direction'],
160
+ _fit_string(dset['reference_direction'], 4, 'reference_direction', 1858),
161
161
  ))
162
162
 
163
163
  fh.write('%-80s\n' % 'NONE')
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
3
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
4
4
 
5
5
 
6
6
  def get_structure_2400(raw=False):
@@ -64,10 +64,11 @@ def _write2400(fh, dset):
64
64
  fh.write('%6i\n%6i%74s\n' % (-1, 2400, ' '))
65
65
  fh.write('%12i%6i%6i%12i\n' % (dset['model_uid'], dset['entity_type'],
66
66
  dset['entity_subtype'], dset['version_number']))
67
- fh.write('%-80s\n' % dset['entity_name'])
68
- fh.write('%-80s\n' % dset['part_number'])
67
+ fh.write('%-80s\n' % _fit_string(dset['entity_name'], 80, 'entity_name', 2400))
68
+ fh.write('%-80s\n' % _fit_string(dset['part_number'], 80, 'part_number', 2400))
69
69
  fh.write('%s\n' % ''.join(['%2i' % v for v in dset['status_mask']]))
70
- fh.write('%-10s%-10s%12i%12i%12i\n' % (dset['date'], dset['time'],
70
+ fh.write('%-10s%-10s%12i%12i%12i\n' % (_fit_string(dset['date'], 10, 'date', 2400),
71
+ _fit_string(dset['time'], 10, 'time', 2400),
71
72
  dset['idm_version_id'], dset['idm_item_id'],
72
73
  dset['primary_parent_uid']))
73
74
  fh.write('%12i\n' % dset['optimization_switches'])
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import math as math
3
3
 
4
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
4
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
5
5
 
6
6
  def get_structure_2414(raw=False):
7
7
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -564,13 +564,13 @@ def _write2414(fh, dset):
564
564
 
565
565
  # Write general fields
566
566
  fh.write('%10i\n' % (dset['analysis_dataset_label'])) #Loadcase number (DS2414_num)
567
- fh.write('%-80s\n' % (dset['analysis_dataset_name'])) #usually with the frequency
567
+ fh.write('%-80s\n' % (_fit_string(dset['analysis_dataset_name'], 80, 'analysis_dataset_name', 2414))) #usually with the frequency
568
568
  fh.write('%10i\n' % (dset['dataset_location']))
569
- fh.write('%-80s\n' % dset['id1'])
570
- fh.write('%-80s\n' % dset['id2'])
571
- fh.write('%-80s\n' % dset['id3']) #usually with the frequency
572
- fh.write('%-80s\n' % dset['id4']) #usually with the loadcase
573
- fh.write('%-80s\n' % dset['id5'])
569
+ fh.write('%-80s\n' % _fit_string(dset['id1'], 80, 'id1', 2414))
570
+ fh.write('%-80s\n' % _fit_string(dset['id2'], 80, 'id2', 2414))
571
+ fh.write('%-80s\n' % _fit_string(dset['id3'], 80, 'id3', 2414)) #usually with the frequency
572
+ fh.write('%-80s\n' % _fit_string(dset['id4'], 80, 'id4', 2414)) #usually with the loadcase
573
+ fh.write('%-80s\n' % _fit_string(dset['id5'], 80, 'id5', 2414))
574
574
 
575
575
  fh.write('%10i%10i%10i%10i%10i%10i\n' % (
576
576
  dset['model_type'],
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
3
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
4
4
 
5
5
  def get_structure_2420(raw=False):
6
6
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -58,7 +58,7 @@ def _write2420(fh, dset):
58
58
  dset = _opt_fields(dset, dict)
59
59
  fh.write('%6i\n%6i%74s\n' % (-1, 2420, ' '))
60
60
  fh.write('%10i\n' % (dset['Part_UID']))
61
- fh.write('%-80s\n' % (dset['Part_Name']))
61
+ fh.write('%-80s\n' % (_fit_string(dset['Part_Name'], 80, 'Part_Name', 2420)))
62
62
 
63
63
  # -- Check Dimensions of input arrays
64
64
  num_CS = len(dset['CS_sys_labels'])
@@ -66,7 +66,7 @@ def _write2420(fh, dset):
66
66
  raise IndexError("Values missing for one or more CS")
67
67
  for node in range(num_CS):
68
68
  fh.write('%10i%10i%10i\n' % (dset['CS_sys_labels'][node], dset['CS_types'][node], dset['CS_colors'][node]))
69
- fh.write('%s\n' % dset['CS_names'][node])
69
+ fh.write('%s\n' % _fit_string(dset['CS_names'][node], 80, 'CS_names', 2420))
70
70
  fh.write('%25.16e%25.16e%25.16e\n' % tuple(dset['CS_matrices'][node][0, :]))
71
71
  fh.write('%25.16e%25.16e%25.16e\n' % tuple(dset['CS_matrices'][node][1, :]))
72
72
  fh.write('%25.16e%25.16e%25.16e\n' % tuple(dset['CS_matrices'][node][2, :]))
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import math
3
3
 
4
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
4
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
5
5
 
6
6
  def get_structure_2429(raw=False):
7
7
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -125,7 +125,7 @@ def _write2429(fh, dset):
125
125
  dset['active_contact_set_no_for_group'],
126
126
  len(dset['entity_type_code'])))
127
127
  # Record 2
128
- fh.write('%-40s\n' % (dset['group_name']))
128
+ fh.write('%-40s\n' % (_fit_string(dset['group_name'], 40, 'group_name', 2429)))
129
129
 
130
130
  # Record 3-N
131
131
  # Write the full lines (which have 4 pairs)
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import math
3
3
 
4
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
4
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
5
5
 
6
6
  def get_structure_2467(raw=False):
7
7
  """(source: https://github.com/victorsndvg/FEconv/blob/master/source/unv/module_dataset_2467.f90)"""
@@ -76,7 +76,7 @@ def _write2467(fh, dset):
76
76
  group['active_contact_set_no_for_group'],
77
77
  len(group['entity_type_code'])))
78
78
  # Record 2
79
- fh.write('%-40s\n' % (group['group_name']))
79
+ fh.write('%-40s\n' % (_fit_string(group['group_name'], 40, 'group_name', 2467)))
80
80
 
81
81
  # Record 3-N
82
82
  # Write the full lines (which have 4 pairs)
@@ -1,6 +1,6 @@
1
1
  import numpy as np
2
2
 
3
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
3
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
4
4
 
5
5
  def get_structure_55(raw=False):
6
6
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -401,11 +401,11 @@ def _write55(fh, dset):
401
401
  data_type = 2
402
402
  # Write strings to the file
403
403
  fh.write('%6i\n%6i%74s\n' % (-1, 55, ' '))
404
- fh.write('%-80s\n' % dset['id1'])
405
- fh.write('%-80s\n' % dset['id2'])
406
- fh.write('%-80s\n' % dset['id3'])
407
- fh.write('%-80s\n' % dset['id4'])
408
- fh.write('%-80s\n' % dset['id5'])
404
+ fh.write('%-80s\n' % _fit_string(dset['id1'], 80, 'id1', 55))
405
+ fh.write('%-80s\n' % _fit_string(dset['id2'], 80, 'id2', 55))
406
+ fh.write('%-80s\n' % _fit_string(dset['id3'], 80, 'id3', 55))
407
+ fh.write('%-80s\n' % _fit_string(dset['id4'], 80, 'id4', 55))
408
+ fh.write('%-80s\n' % _fit_string(dset['id5'], 80, 'id5', 55))
409
409
  fh.write('%10i%10i%10i%10i%10i%10i\n' %
410
410
  (dset['model_type'], dset['analysis_type'], dset['data_ch'],
411
411
  dset['spec_data_type'], data_type, n_data_per_node))
@@ -3,7 +3,7 @@ import struct
3
3
  import sys
4
4
  import os
5
5
 
6
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
6
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
7
7
 
8
8
  def get_structure_58(raw=False):
9
9
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -827,7 +827,8 @@ that takes on a binary format in the future.
827
827
  def _write58(fh, dset, mode='add', _filename=None, force_double=True):
828
828
  """Writes function at nodal DOF - data-set 58 - to an open file fh."""
829
829
  try:
830
- if not (dset['func_type'] in [0, 1, 2, 3, 4, 6, 9]):
830
+ # function types 0-27 of the format; the record layout does not depend on the type
831
+ if not (dset['func_type'] in range(28)):
831
832
  raise ValueError('Unsupported function type')
832
833
  # handle optional fields - only those that are not calculated
833
834
  # automatically
@@ -888,18 +889,26 @@ def _write58(fh, dset, mode='add', _filename=None, force_double=True):
888
889
 
889
890
  if dset['ord_data_type'] in [4, 6]: # double precision
890
891
  is_double = True
891
- n_bytes = num_pts * 8
892
+ n_value_bytes = 8
892
893
  elif dset['ord_data_type'] in [2, 5]: # sigle precision
893
894
  is_double = False
894
- n_bytes = num_pts * 4
895
-
896
- if 'n_bytes' in dset.keys():
897
- dset['n_bytes'] = n_bytes
895
+ n_value_bytes = 4
898
896
 
899
897
  ord_data_type = dset['ord_data_type']
900
898
 
901
899
  is_even = bool(dset['abscissa_spacing']) # handling even/uneven abscissa spacing manually
902
900
 
901
+ # number of bytes of the binary data (58b): every value written, i.e. the
902
+ # real and imaginary part for complex data and the abscissa for uneven spacing;
903
+ # the abscissa is always real single precision
904
+ n_ord_values = 1 if is_r else 2
905
+ n_bytes = num_pts * n_ord_values * n_value_bytes
906
+ if not is_even:
907
+ n_bytes += num_pts * 4
908
+
909
+ if 'n_bytes' in dset.keys():
910
+ dset['n_bytes'] = n_bytes
911
+
903
912
  # handling abscissa spacing automatically
904
913
  # is_even = len( set( [ dset['x'][ii]-dset['x'][ii-1] for ii in range(1,len(dset['x'])) ] ) ) == 1
905
914
  dset['abscissa_min'] = dset['x'][0]
@@ -913,34 +922,39 @@ def _write58(fh, dset, mode='add', _filename=None, force_double=True):
913
922
  fh.write('b%6i%6i%12i%12i%6i%6i%12i%12i\n' % (bo, 2, 11, n_bytes, 0, 0, 0, 0))
914
923
  else:
915
924
  fh.write('%74s\n' % ' ')
916
- fh.write('%-80s\n' % dset['id1'])
917
- fh.write('%-80s\n' % dset['id2'])
918
- fh.write('%-80s\n' % dset['id3'])
919
- fh.write('%-80s\n' % dset['id4'])
920
- fh.write('%-80s\n' % dset['id5'])
925
+ fh.write('%-80s\n' % _fit_string(dset['id1'], 80, 'id1', 58))
926
+ fh.write('%-80s\n' % _fit_string(dset['id2'], 80, 'id2', 58))
927
+ fh.write('%-80s\n' % _fit_string(dset['id3'], 80, 'id3', 58))
928
+ fh.write('%-80s\n' % _fit_string(dset['id4'], 80, 'id4', 58))
929
+ fh.write('%-80s\n' % _fit_string(dset['id5'], 80, 'id5', 58))
921
930
  fh.write('%5i%10i%5i%10i %10s%10i%4i %10s%10i%4i\n' %
922
931
  (dset['func_type'], dset['func_id'], dset['ver_num'], dset['load_case_id'],
923
- dset['rsp_ent_name'], dset['rsp_node'], dset['rsp_dir'], dset['ref_ent_name'],
932
+ _fit_string(dset['rsp_ent_name'], 10, 'rsp_ent_name', 58), dset['rsp_node'], dset['rsp_dir'],
933
+ _fit_string(dset['ref_ent_name'], 10, 'ref_ent_name', 58),
924
934
  dset['ref_node'], dset['ref_dir']))
925
935
  fh.write('%10i%10i%10i%13.5e%13.5e%13.5e\n' % (ord_data_type, num_pts, is_even,
926
936
  is_even * dset['abscissa_min'], is_even * dx,
927
937
  dset['z_axis_value']))
928
938
  fh.write('%10i%5i%5i%5i %-20s %-20s\n' % (dset['abscissa_spec_data_type'],
929
939
  dset['abscissa_len_unit_exp'], dset['abscissa_force_unit_exp'],
930
- dset['abscissa_temp_unit_exp'], dset['abscissa_axis_lab'],
931
- dset['abscissa_axis_units_lab']))
940
+ dset['abscissa_temp_unit_exp'],
941
+ _fit_string(dset['abscissa_axis_lab'], 20, 'abscissa_axis_lab', 58),
942
+ _fit_string(dset['abscissa_axis_units_lab'], 20, 'abscissa_axis_units_lab', 58)))
932
943
  fh.write('%10i%5i%5i%5i %-20s %-20s\n' % (dset['ordinate_spec_data_type'],
933
944
  dset['ordinate_len_unit_exp'], dset['ordinate_force_unit_exp'],
934
- dset['ordinate_temp_unit_exp'], dset['ordinate_axis_lab'],
935
- dset['ordinate_axis_units_lab']))
945
+ dset['ordinate_temp_unit_exp'],
946
+ _fit_string(dset['ordinate_axis_lab'], 20, 'ordinate_axis_lab', 58),
947
+ _fit_string(dset['ordinate_axis_units_lab'], 20, 'ordinate_axis_units_lab', 58)))
936
948
  fh.write('%10i%5i%5i%5i %-20s %-20s\n' % (dset['orddenom_spec_data_type'],
937
949
  dset['orddenom_len_unit_exp'], dset['orddenom_force_unit_exp'],
938
- dset['orddenom_temp_unit_exp'], dset['orddenom_axis_lab'],
939
- dset['orddenom_axis_units_lab']))
950
+ dset['orddenom_temp_unit_exp'],
951
+ _fit_string(dset['orddenom_axis_lab'], 20, 'orddenom_axis_lab', 58),
952
+ _fit_string(dset['orddenom_axis_units_lab'], 20, 'orddenom_axis_units_lab', 58)))
940
953
  fh.write('%10i%5i%5i%5i %-20s %-20s\n' % (dset['z_axis_spec_data_type'],
941
954
  dset['z_axis_len_unit_exp'], dset['z_axis_force_unit_exp'],
942
- dset['z_axis_temp_unit_exp'], dset['z_axis_axis_lab'],
943
- dset['z_axis_axis_units_lab']))
955
+ dset['z_axis_temp_unit_exp'],
956
+ _fit_string(dset['z_axis_axis_lab'], 20, 'z_axis_axis_lab', 58),
957
+ _fit_string(dset['z_axis_axis_units_lab'], 20, 'z_axis_axis_units_lab', 58)))
944
958
  if is_r:
945
959
  if is_even:
946
960
  data = dset['data'].copy()
@@ -958,23 +972,30 @@ def _write58(fh, dset, mode='add', _filename=None, force_double=True):
958
972
  data[0:-2:3] = dset['x']
959
973
  data[1:-1:3] = dset['data'].real
960
974
  data[2::3] = dset['data'].imag
961
- # always write data in double precision
962
975
  if dset['binary']:
963
976
  fh.close()
964
- if mode.lower() == 'overwrite':
965
- fh = open(_filename, 'wb')
966
- elif mode.lower() == 'add':
967
- fh = open(_filename, 'ab')
968
- # write data
977
+ # the header was just written to the file: append in both modes
978
+ fh = open(_filename, 'ab')
979
+ # write data in the precision given by the ordinate data type
980
+ if is_double:
981
+ value_format = 'd'
982
+ else:
983
+ value_format = 'f'
969
984
  if bo == 1:
970
- [fh.write(struct.pack('<d', datai)) for datai in data]
985
+ byte_order = '<'
986
+ else:
987
+ byte_order = '>'
988
+ if is_even:
989
+ fh.write(data.astype(byte_order + value_format).tobytes())
971
990
  else:
972
- [fh.write(struct.pack('>d', datai)) for datai in data]
991
+ # uneven spacing: the abscissa is always real single precision
992
+ rows = np.zeros(num_pts, [('x', byte_order + 'f'),
993
+ ('y', byte_order + value_format, (n_ord_values,))])
994
+ rows['x'] = dset['x']
995
+ rows['y'] = data.reshape(num_pts, -1)[:, 1:]
996
+ fh.write(rows.tobytes())
973
997
  fh.close()
974
- if mode.lower() == 'overwrite':
975
- fh = open(_filename, 'wt', encoding='utf-8')
976
- elif mode.lower() == 'add':
977
- fh = open(_filename, 'at', encoding='utf-8')
998
+ fh = open(_filename, 'at', encoding='utf-8')
978
999
  else:
979
1000
  if is_double: # write double precision
980
1001
  n4_blocks = len(data) // 4
@@ -1088,6 +1109,15 @@ def _extract58(block_data, header_only=False):
1088
1109
  # single precision - 4 bytes, note: sometimes split_data wrongly contains CR/LF and
1089
1110
  # therefore the length of split_data is not divisible by 4
1090
1111
  values = np.asarray(struct.unpack('%c%sf' % (bo, int(len(split_data) / 4)), split_data[:(len(split_data) // 4)*4]), 'd')
1112
+ elif (dset['abscissa_spacing'] == 0) and \
1113
+ (len(split_data) < dset['num_pts'] * (8 + 8 * (1 if dset['ord_data_type'] == 4 else 2))):
1114
+ # double precision, uneven abscissa: the abscissa is real single precision
1115
+ # (pyuff 2.5.8 and older wrote it in double precision; those records are
1116
+ # longer and are read below)
1117
+ n_ord_values = 1 if dset['ord_data_type'] == 4 else 2
1118
+ rows = np.frombuffer(split_data[:dset['num_pts'] * (4 + 8 * n_ord_values)],
1119
+ [('x', bo + 'f'), ('y', bo + 'd', (n_ord_values,))])
1120
+ values = np.column_stack((rows['x'], rows['y'])).ravel().astype('d')
1091
1121
  else:
1092
1122
  # double precision - 8 bytes, note: sometimes split_data wrongly contains CR/LF and
1093
1123
  # therefore the length of split_data is not divisible by 8
@@ -1,7 +1,7 @@
1
1
  import numpy as np
2
2
  import os
3
3
 
4
- from ..tools import _opt_fields, _parse_header_line, check_dict_for_none
4
+ from ..tools import _opt_fields, _parse_header_line, check_dict_for_none, _fit_string
5
5
 
6
6
  def get_structure_82(raw=False):
7
7
  """(source: https://www.ceas3.uc.edu/sdrluff/"""
@@ -57,7 +57,7 @@ def _write82(fh, dset):
57
57
  n_nodes = np.sum((dset['nodes'][1:] - dset['nodes'][:-1]) != 0) + 1
58
58
  fh.write('%6i\n%6i%74s\n' % (-1, 82, ' '))
59
59
  fh.write('%10i%10i%10i\n' % (dset['trace_num'], n_nodes, dset['color']))
60
- fh.write('%-80s\n' % dset['id'])
60
+ fh.write('%-80s\n' % _fit_string(dset['id'], 80, 'id', 82))
61
61
  sl = 0
62
62
  n8_blocks = n_nodes // 8
63
63
  rem_lines = n_nodes % 8
@@ -297,11 +297,15 @@ class UFF:
297
297
  calculated automatically and the dset is updated - as dset is actually
298
298
  an alias (aka pointer), this is reflected at the caller too.
299
299
 
300
- The required fields are:
301
- dset - dictionary representing the data-set
302
- mode - 'add' or 'overwrite'
303
- force_double - True or False (default True). Single precision should be avoided,
304
- therefore by default data is saved with double precision.
300
+ NaN and infinite values in ``data`` are written as 0 and as the
301
+ largest finite float, respectively, with a warning.
302
+
303
+ Parameters:
304
+
305
+ - ``dsets``: a dictionary, or a list of dictionaries, one per data-set
306
+ - ``mode``: ``'add'`` (default) or ``'overwrite'``
307
+ - ``force_double``: ``True`` (default) or ``False``; single precision should be
308
+ avoided, therefore by default data is saved with double precision.
305
309
  """
306
310
  if (not type(dsets).__name__ == 'list'):
307
311
  dsets = [dsets]
@@ -439,6 +443,13 @@ class UFF:
439
443
  raise Exception('Data-set\'s dictionary is missing the required \'type\' key')
440
444
  # handle nan or inf
441
445
  if 'data' in dset.keys():
446
+ data = np.asarray(dset['data'])
447
+ if np.issubdtype(data.dtype, np.inexact):
448
+ n_not_finite = np.count_nonzero(~np.isfinite(data))
449
+ if n_not_finite:
450
+ warnings.warn('Data-set %s: %i of %i data values are NaN or infinite; '
451
+ 'NaN is written as 0 and infinity as the largest finite '
452
+ 'float.' % (set_type, n_not_finite, data.size), stacklevel=3)
442
453
  dset['data'] = np.nan_to_num(dset['data'])
443
454
 
444
455
  if set_type == 15:
@@ -1,5 +1,6 @@
1
1
  import pyuff
2
2
  import numpy as np
3
+ import warnings
3
4
 
4
5
  def _opt_fields(dict, fields_dict):
5
6
  """Sets the optional fields of the dict dictionary.
@@ -118,6 +119,24 @@ def _write_record(fh, values, formats, multiline=False, fstring=True):
118
119
 
119
120
  fh.write(to_write)
120
121
 
122
+ def _fit_string(value, width, field, set_type):
123
+ """Returns the value as a string of at most width characters.
124
+
125
+ A longer string does not fit the fixed-width field of the record; it is
126
+ truncated with a warning naming the data-set and the field.
127
+
128
+ :param value: value to be written to a text field
129
+ :param width: width of the field (characters)
130
+ :param field: name of the field (key of the dataset dictionary)
131
+ :param set_type: data-set number
132
+ """
133
+ value = str(value)
134
+ if len(value) > width:
135
+ warnings.warn('Data-set %s: %s is longer than %i characters and is truncated to %r.'
136
+ % (set_type, field, width, value[:width]), stacklevel=5)
137
+ value = value[:width]
138
+ return value
139
+
121
140
  def check_dict_for_none(dataset):
122
141
  dataset1 = {}
123
142
  for k, v in dataset.items():
File without changes
File without changes
File without changes
File without changes
File without changes