pypsbuilder 2.3.6__tar.gz → 2.4.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.
Files changed (57) hide show
  1. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/CHANGELOG.md +6 -0
  2. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/PKG-INFO +7 -1
  3. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/conf.py +47 -36
  4. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/__init__.py +1 -1
  5. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/psexplorer.py +220 -72
  6. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/PKG-INFO +7 -1
  7. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/setup.cfg +1 -1
  8. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/setup.py +17 -24
  9. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/AUTHORS.md +0 -0
  10. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/CONTRIBUTING.md +0 -0
  11. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/LICENSE +0 -0
  12. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/MANIFEST.in +0 -0
  13. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/README.md +0 -0
  14. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/Makefile +0 -0
  15. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.Dogmin.rst +0 -0
  16. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.InvPoint.rst +0 -0
  17. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.PTsection.rst +0 -0
  18. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.PXsection.rst +0 -0
  19. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.TCResult.rst +0 -0
  20. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.TCResultSet.rst +0 -0
  21. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.TXsection.rst +0 -0
  22. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psclasses.UniLine.rst +0 -0
  23. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psexplorer.PTPS.rst +0 -0
  24. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psexplorer.PXPS.rst +0 -0
  25. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.psexplorer.TXPS.rst +0 -0
  26. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.tcapi.TC34API.rst +0 -0
  27. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api/pypsbuilder.tcapi.TC35API.rst +0 -0
  28. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/api.rst +0 -0
  29. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/authors.rst +0 -0
  30. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/command-line.rst +0 -0
  31. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/index.rst +0 -0
  32. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/installation.rst +0 -0
  33. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/psbuilders-tutorial.rst +0 -0
  34. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/docs/psexplorer-tutorial.rst +0 -0
  35. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/images/__init__.py +0 -0
  36. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/images/ptbuilder.png +0 -0
  37. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/images/pxbuilder.png +0 -0
  38. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/images/pypsbuilder.png +0 -0
  39. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/images/txbuilder.png +0 -0
  40. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/psbuilders.py +0 -0
  41. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/psclasses.py +0 -0
  42. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/tcapi.py +0 -0
  43. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/tcinit.py +0 -0
  44. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/tests/__init__.py +0 -0
  45. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/tests/test_pclasses.py +0 -0
  46. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_addinv.py +0 -0
  47. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_adduni.py +0 -0
  48. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_ptbuilder.py +0 -0
  49. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_pxbuilder.py +0 -0
  50. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_txbuilder.py +0 -0
  51. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder/ui_uniguess.py +0 -0
  52. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/SOURCES.txt +0 -0
  53. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/dependency_links.txt +0 -0
  54. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/entry_points.txt +0 -0
  55. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/not-zip-safe +0 -0
  56. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/requires.txt +0 -0
  57. {pypsbuilder-2.3.6 → pypsbuilder-2.4.0}/pypsbuilder.egg-info/top_level.txt +0 -0
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
  All notable pypsbuilder changes.
3
3
 
4
+ ## [2.4.0] - 2023-12-21
5
+ ### Fixed
6
+ - important collect_grid_data bug causing isopleths twisting fixed.
7
+ - collect_ptpath missing field check added
8
+ - remove_grid_data method added
9
+ - isopleths bivariate spline method added
4
10
  ## [2.3.6] - 2023-12-13
5
11
  ### Added
6
12
  - search_composition method added to PTPS to find best estimate for isopleths intersection
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypsbuilder
3
- Version: 2.3.6
3
+ Version: 2.4.0
4
4
  Summary: THERMOCALC front-end for constructing and analyzing PT pseudosections
5
5
  Home-page: https://github.com/ondrolexa/pypsbuilder
6
6
  Author: Ondrej Lexa
@@ -88,6 +88,12 @@ pypsbuilder is free software: you can redistribute it and/or modify it under the
88
88
  # Changelog
89
89
  All notable pypsbuilder changes.
90
90
 
91
+ ## [2.4.0] - 2023-12-21
92
+ ### Fixed
93
+ - important collect_grid_data bug causing isopleths twisting fixed.
94
+ - collect_ptpath missing field check added
95
+ - remove_grid_data method added
96
+ - isopleths bivariate spline method added
91
97
  ## [2.3.6] - 2023-12-13
92
98
  ### Added
93
99
  - search_composition method added to PTPS to find best estimate for isopleths intersection
@@ -20,9 +20,9 @@
20
20
  import os
21
21
  import sys
22
22
 
23
- sys.path.insert(0, os.path.abspath('.'))
24
- sys.path.insert(0, os.path.abspath('..'))
25
- sys.path.insert(0, os.path.abspath('../'))
23
+ sys.path.insert(0, os.path.abspath("."))
24
+ sys.path.insert(0, os.path.abspath(".."))
25
+ sys.path.insert(0, os.path.abspath("../"))
26
26
 
27
27
 
28
28
  # -- General configuration ------------------------------------------------
@@ -35,42 +35,49 @@ sys.path.insert(0, os.path.abspath('../'))
35
35
  # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
36
36
  # ones.
37
37
  extensions = [
38
- 'sphinx.ext.todo',
39
- 'sphinx.ext.autodoc',
40
- 'sphinx.ext.napoleon',
41
- 'sphinx.ext.autosummary'
38
+ "sphinx.ext.todo",
39
+ "sphinx.ext.autodoc",
40
+ "sphinx.ext.napoleon",
41
+ "sphinx.ext.autosummary",
42
42
  ]
43
43
 
44
44
  napoleon_google_docstring = True
45
45
  napoleon_numpy_docstring = False
46
46
 
47
- autodoc_mock_imports = ['numpy', 'matplotlib', 'scipy', 'shapely',
48
- 'networkx', 'tqdm', 'mpl_toolkits']
47
+ autodoc_mock_imports = [
48
+ "numpy",
49
+ "matplotlib",
50
+ "scipy",
51
+ "shapely",
52
+ "networkx",
53
+ "tqdm",
54
+ "mpl_toolkits",
55
+ ]
49
56
  # Add any paths that contain templates here, relative to this directory.
50
- templates_path = ['_templates']
57
+ templates_path = ["_templates"]
51
58
 
52
59
  # The suffix(es) of source filenames.
53
60
  # You can specify multiple suffix as a list of string:
54
61
  #
55
62
  # source_suffix = ['.rst', '.md']
56
- source_suffix = '.rst'
63
+ source_suffix = ".rst"
57
64
 
58
65
  # The master toctree document.
59
- master_doc = 'index'
66
+ master_doc = "index"
60
67
 
61
68
  # General information about the project.
62
- project = 'pypsbuilder'
63
- copyright = '2020, Ondrej Lexa'
64
- author = 'Ondrej Lexa'
69
+ project = "pypsbuilder"
70
+ copyright = "2020, Ondrej Lexa"
71
+ author = "Ondrej Lexa"
65
72
 
66
73
  # The version info for the project you're documenting, acts as replacement for
67
74
  # |version| and |release|, also used in various other places throughout the
68
75
  # built documents.
69
76
  #
70
77
  # The short X.Y version.
71
- version = '2.3'
78
+ version = "2.4"
72
79
  # The full version, including alpha/beta/rc tags.
73
- release = '2.3.6'
80
+ release = "2.4.0"
74
81
 
75
82
  # The language for content autogenerated by Sphinx. Refer to documentation
76
83
  # for a list of supported languages.
@@ -82,22 +89,21 @@ language = None
82
89
  # List of patterns, relative to source directory, that match files and
83
90
  # directories to ignore when looking for source files.
84
91
  # This patterns also effect to html_static_path and html_extra_path
85
- exclude_patterns = ['_build', '**.ipynb_checkpoints', 'Thumbs.db', '.DS_Store']
92
+ exclude_patterns = ["_build", "**.ipynb_checkpoints", "Thumbs.db", ".DS_Store"]
86
93
 
87
94
  # The name of the Pygments (syntax highlighting) style to use.
88
- pygments_style = 'sphinx'
95
+ pygments_style = "sphinx"
89
96
 
90
97
  # If true, `todo` and `todoList` produce output, else they produce nothing.
91
98
  todo_include_todos = True
92
99
 
93
- autodoc_default_options = {'member-order': 'bysource',
94
- 'show-inheritance': True}
100
+ autodoc_default_options = {"member-order": "bysource", "show-inheritance": True}
95
101
  autosummary_generate = True
96
102
  # -- Options for HTML output ----------------------------------------------
97
103
 
98
104
  # The theme to use for HTML and HTML Help pages. See the documentation for
99
105
  # a list of builtin themes.
100
- html_theme = 'sphinx_rtd_theme'
106
+ html_theme = "sphinx_rtd_theme"
101
107
 
102
108
  # Theme options are theme-specific and customize the look and feel of a theme
103
109
  # further. For a list of options available for each theme, see the
@@ -108,7 +114,7 @@ html_theme = 'sphinx_rtd_theme'
108
114
  # Add any paths that contain custom static files (such as style sheets) here,
109
115
  # relative to this directory. They are copied after the builtin static files,
110
116
  # so a file named "default.css" will overwrite the builtin "default.css".
111
- html_static_path = ['_static']
117
+ html_static_path = ["_static"]
112
118
 
113
119
  # Hide source link
114
120
  html_show_sourcelink = False
@@ -116,7 +122,7 @@ html_show_sourcelink = False
116
122
  # -- Options for HTMLHelp output ------------------------------------------
117
123
 
118
124
  # Output file base name for HTML help builder.
119
- htmlhelp_basename = 'pypsbuilderdoc'
125
+ htmlhelp_basename = "pypsbuilderdoc"
120
126
 
121
127
 
122
128
  # -- Options for LaTeX output ---------------------------------------------
@@ -125,15 +131,12 @@ latex_elements = {
125
131
  # The paper size ('letterpaper' or 'a4paper').
126
132
  #
127
133
  # 'papersize': 'letterpaper',
128
-
129
134
  # The font size ('10pt', '11pt' or '12pt').
130
135
  #
131
136
  # 'pointsize': '10pt',
132
-
133
137
  # Additional stuff for the LaTeX preamble.
134
138
  #
135
139
  # 'preamble': '',
136
-
137
140
  # Latex figure (float) alignment
138
141
  #
139
142
  # 'figure_align': 'htbp',
@@ -143,8 +146,13 @@ latex_elements = {
143
146
  # (source start file, target name, title,
144
147
  # author, documentclass [howto, manual, or own class]).
145
148
  latex_documents = [
146
- (master_doc, 'pypsbuilder.tex', 'pypsbuilder Documentation',
147
- 'Ondrej Lexa', 'manual'),
149
+ (
150
+ master_doc,
151
+ "pypsbuilder.tex",
152
+ "pypsbuilder Documentation",
153
+ "Ondrej Lexa",
154
+ "manual",
155
+ ),
148
156
  ]
149
157
 
150
158
 
@@ -152,10 +160,7 @@ latex_documents = [
152
160
 
153
161
  # One entry per manual page. List of tuples
154
162
  # (source start file, name, description, authors, manual section).
155
- man_pages = [
156
- (master_doc, 'pypsbuilder', 'pypsbuilder Documentation',
157
- [author], 1)
158
- ]
163
+ man_pages = [(master_doc, "pypsbuilder", "pypsbuilder Documentation", [author], 1)]
159
164
 
160
165
 
161
166
  # -- Options for Texinfo output -------------------------------------------
@@ -164,7 +169,13 @@ man_pages = [
164
169
  # (source start file, target name, title, author,
165
170
  # dir menu entry, description, category)
166
171
  texinfo_documents = [
167
- (master_doc, 'pypsbuilder', 'pypsbuilder Documentation',
168
- author, 'pypsbuilder', 'One line description of project.',
169
- 'Miscellaneous'),
172
+ (
173
+ master_doc,
174
+ "pypsbuilder",
175
+ "pypsbuilder Documentation",
176
+ author,
177
+ "pypsbuilder",
178
+ "One line description of project.",
179
+ "Miscellaneous",
180
+ ),
170
181
  ]
@@ -22,7 +22,7 @@ __all__ = (
22
22
  "get_tcapi",
23
23
  )
24
24
 
25
- __version__ = "2.3.6"
25
+ __version__ = "2.4.0"
26
26
  __author__ = "Ondrej Lexa"
27
27
  __copyright__ = "© Ondrej Lexa 2023"
28
28
  __email__ = "lexa.ondrej@gmail.com"
@@ -46,7 +46,7 @@ from matplotlib import ticker
46
46
 
47
47
  from shapely.geometry import MultiPoint, Point, LineString
48
48
  from shapely.ops import linemerge
49
- from scipy.interpolate import Rbf, interp1d
49
+ from scipy.interpolate import Rbf, interp1d, SmoothBivariateSpline
50
50
  from scipy.linalg import LinAlgWarning, lstsq
51
51
  from scipy.interpolate import griddata # interp2d
52
52
  from tqdm import tqdm, trange
@@ -539,17 +539,18 @@ class PS:
539
539
  if self.gridded:
540
540
  for ix, grid in self.grids.items():
541
541
  if key in grid.masks:
542
- results = grid.gridcalcs[grid.masks[key] & (grid.status == 1)]
542
+ ok = grid.masks[key] & (grid.status == 1)
543
+ results = grid.gridcalcs[ok]
543
544
  if len(results) > 0:
544
545
  if phase in results[0].phases:
545
546
  gdt = zip(
546
- grid.xg[grid.masks[key]],
547
- grid.yg[grid.masks[key]],
547
+ grid.xg[ok],
548
+ grid.yg[ok],
548
549
  results,
549
- grid.status[grid.masks[key]],
550
+ grid.status[ok],
550
551
  )
551
- for x, y, res, ok in gdt:
552
- if ok == 1:
552
+ for x, y, res, status in gdt:
553
+ if status == 1:
553
554
  dt["pts"].append((x, y))
554
555
  dt["data"].append(eval_expr(expr, res[phase]))
555
556
  # else:
@@ -912,12 +913,15 @@ class PS:
912
913
  """
913
914
  if self.check_phase_expr(phase, expr):
914
915
  dt = self.collect_data(key, phase, expr, which=which)
915
- x, y = np.array(dt["pts"]).T
916
- fig, ax = plt.subplots()
917
- pts = ax.scatter(x, y, c=dt["data"])
918
- ax.set_title("{} - {}({})".format(" ".join(key), phase, expr))
919
- plt.colorbar(pts)
920
- plt.show()
916
+ if dt["pts"]:
917
+ x, y = np.array(dt["pts"]).T
918
+ fig, ax = plt.subplots()
919
+ pts = ax.scatter(x, y, c=dt["data"])
920
+ ax.set_title("{} - {}({})".format(" ".join(key), phase, expr))
921
+ plt.colorbar(pts)
922
+ plt.show()
923
+ else:
924
+ print("No data collected")
921
925
 
922
926
  def show_grid(
923
927
  self, phase, expr=None, interpolation=None, label=False, skiplabels=0, labelfs=6
@@ -991,6 +995,8 @@ class PS:
991
995
  fig=None,
992
996
  fig_kw={},
993
997
  isopleths=False,
998
+ which=7,
999
+ smooth=0,
994
1000
  ):
995
1001
  """Function to plot root squared error of calculated and estimated values.
996
1002
 
@@ -1013,6 +1019,9 @@ class PS:
1013
1019
  Default None
1014
1020
  fig_kw: dict passed to subplots method.
1015
1021
  isopleths (bool): When True, searched isoplots are shown.Default False
1022
+ which (int): Bitopt defining from where data are collected. 0 bit -
1023
+ invariant points, 1 bit - uniariant lines and 2 bit - GridData
1024
+ points
1016
1025
 
1017
1026
  Example:
1018
1027
  >>> pt.search_composition(
@@ -1027,16 +1036,25 @@ class PS:
1027
1036
  err = []
1028
1037
  for phase, expr, val in zip(args[::3], args[1::3], args[2::3]):
1029
1038
  if self.check_phase_expr(phase, expr):
1030
- err.append((self.get_gridded(phase, expr) - val) ** 2)
1039
+ err.append(
1040
+ (
1041
+ self.get_gridded(
1042
+ phase, expr, which=which, smooth=smooth
1043
+ )
1044
+ - val
1045
+ )
1046
+ ** 2
1047
+ )
1031
1048
 
1032
- err = np.sqrt(sum(err))
1049
+ err = np.sqrt(sum(err)) / len(err)
1033
1050
  r, c = np.unravel_index(np.nanargmin(err), err.shape)
1034
1051
  p = self.yspace[r]
1035
1052
  T = self.xspace[c]
1053
+ minerr = err[r, c]
1036
1054
  if geterror:
1037
1055
  return err
1038
1056
  elif getpt:
1039
- return p, T, np.nanmin(err)
1057
+ return p, T, minerr
1040
1058
  else:
1041
1059
  if fig is None:
1042
1060
  fig, ax = plt.subplots(**fig_kw)
@@ -1064,7 +1082,7 @@ class PS:
1064
1082
  ax.set_ylim(self.yrange)
1065
1083
  fig.colorbar(im)
1066
1084
 
1067
- ax.set_title(f"RMSE - Min at {T} {p}")
1085
+ ax.set_title(f"RMSE - MinErr={minerr:g} at {T} {p}")
1068
1086
  fig.tight_layout()
1069
1087
  plt.show()
1070
1088
  else:
@@ -1245,6 +1263,38 @@ class PS:
1245
1263
  print(output)
1246
1264
  return res[0]
1247
1265
 
1266
+ def remove_grid_data(self, key, phase, expr):
1267
+ """Remove calculated data from grid and mark as failed
1268
+
1269
+ Note: Click on all data to be discarded by mouse and finish be Enter
1270
+
1271
+ Args:
1272
+ key (frozenset): Key identifying divariant field
1273
+ phase (str): Phase or end-member named
1274
+ expr (str): Expression to evaluate. It could use any variable
1275
+ existing for given phase. Check `all_data_keys` property for
1276
+ possible variables.
1277
+ """
1278
+
1279
+ if self.gridded:
1280
+ dt = self.collect_data(key, phase, expr, which=4)
1281
+ x, y = np.array(dt["pts"]).T
1282
+ fig, ax = plt.subplots()
1283
+ pts = ax.scatter(x, y, c=dt["data"])
1284
+ plt.colorbar(pts)
1285
+ xy = plt.ginput(0)
1286
+ plt.close(fig)
1287
+ for x, y in xy:
1288
+ ix = self.get_section_id(x, y)
1289
+ if ix is not None:
1290
+ r, c = self.grids[ix].get_indexes(x, y)
1291
+ self.grids[ix].status[r, c] = 0
1292
+ # self.grids[ix].masks[key][r, c] = False
1293
+ self.grids[ix].gridcalcs[r, c] = None
1294
+ self.grids[ix].delta[r, c] = np.nan
1295
+ else:
1296
+ print("Not yet gridded...")
1297
+
1248
1298
  def ginput_path(self, label=False, skiplabels=0, labelfs=6):
1249
1299
  """Collect Path data by mouse digitizing.
1250
1300
 
@@ -1293,7 +1343,8 @@ class PS:
1293
1343
  invariant points, 1 bit - uniariant lines and 2 bit - GridData
1294
1344
  points. Default 7 (all data)
1295
1345
  method: Interpolation method. Default is 'rbf', other option is
1296
- 'quadratic', which uses least-square fit to quadratic surface.
1346
+ 'quadratic', which uses least-square fit to quadratic surface
1347
+ or 'spline' for bivariate spline interpolation.
1297
1348
  rbf_func: Default 'thin_plate'. See scipy.interpolation.Rbf
1298
1349
  smooth (int): Values greater than zero increase the smoothness
1299
1350
  of the approximation. 0 is for interpolation (default),
@@ -1301,7 +1352,9 @@ class PS:
1301
1352
  epsilon (int): Adjustable constant for gaussian or multiquadrics
1302
1353
  functions - defaults to approximate average distance between
1303
1354
  nodes (which is a good start).
1355
+ degree (int): Degrees of the bivariate spline. Default is 3.
1304
1356
  refine (int): Degree of grid refinement. Default 1
1357
+ filter_outliers (bool): Whether to filter outliers. Defaut False.
1305
1358
  filled (bool): Whether to contours should be filled. Defaut True.
1306
1359
  filled_over (bool): Whether to overlay contourline over filled
1307
1360
  contours. Defaut False.
@@ -1346,6 +1399,8 @@ class PS:
1346
1399
  gradient = kwargs.get("gradient", False)
1347
1400
  dx = kwargs.get("dx", True)
1348
1401
  only = kwargs.get("only", None)
1402
+ filter_outliers = kwargs.get("only", False)
1403
+ degree = kwargs.get("degree", 3)
1349
1404
  refine = kwargs.get("refine", 1)
1350
1405
  method = kwargs.get("method", "rbf")
1351
1406
  rbf_func = kwargs.get("rbf_func", "thin_plate")
@@ -1424,9 +1479,22 @@ class PS:
1424
1479
  self.gridystep / refine,
1425
1480
  )
1426
1481
  tg, pg = np.meshgrid(ttspace, ppspace)
1427
- x, y = np.array(recs[key]["pts"]).T
1428
- pts = recs[key]["pts"]
1429
- data = recs[key]["data"]
1482
+ pts = np.array(recs[key]["pts"])
1483
+ x, y = pts.T
1484
+ data = np.array(recs[key]["data"])
1485
+ # filter outliers
1486
+ if filter_outliers:
1487
+ A = np.c_[np.ones_like(x), x, y, x * y, x**2, y**2]
1488
+ C, _, _, _ = lstsq(A, data)
1489
+ # evaluate it on a grid
1490
+ vals = np.dot(
1491
+ np.c_[np.ones_like(x), x, y, x * y, x**2, y**2], C
1492
+ )
1493
+ err = abs(vals - data)
1494
+ ok = err < err.std()
1495
+ pts = pts[ok, :]
1496
+ x, y = pts.T
1497
+ data = data[ok]
1430
1498
  try:
1431
1499
  if method == "quadratic":
1432
1500
  tgg = tg.flatten()
@@ -1445,6 +1513,11 @@ class PS:
1445
1513
  ],
1446
1514
  C,
1447
1515
  ).reshape(tg.shape)
1516
+ elif method == "spline":
1517
+ interp = SmoothBivariateSpline(
1518
+ x, self.ratio * y, data, kx=degree, ky=degree
1519
+ )
1520
+ zg = interp(tg, self.ratio * pg)
1448
1521
  else:
1449
1522
  with warnings.catch_warnings():
1450
1523
  warnings.filterwarnings("error")
@@ -1461,29 +1534,19 @@ class PS:
1461
1534
  if self.show_errors:
1462
1535
  print(e)
1463
1536
  try:
1464
- if method == "quadratic":
1465
- print(
1466
- "Using nearest method in {}".format(
1467
- " ".join(sorted(list(key)))
1468
- )
1469
- )
1470
- zg = griddata(
1471
- np.array(pts),
1472
- data,
1473
- (tg, pg),
1474
- method="nearest",
1475
- rescale=True,
1476
- )
1477
- else:
1478
- # preprocess with griddata
1479
- zg_tmp = griddata(
1480
- pts, data, (tg, pg), method="linear", rescale=True
1481
- )
1537
+ zg = griddata(
1538
+ np.array(pts),
1539
+ data,
1540
+ (tg, pg),
1541
+ method="nearest",
1542
+ rescale=True,
1543
+ )
1544
+ if method == "rbf":
1482
1545
  # locate valid data
1483
- ri, ci = np.nonzero(np.isfinite(zg_tmp))
1546
+ ri, ci = np.nonzero(np.isfinite(zg))
1484
1547
  x, y, z = np.array(
1485
1548
  [
1486
- [tg[r, c], pg[r, c], zg_tmp[r, c]]
1549
+ [tg[r, c], pg[r, c], zg[r, c]]
1487
1550
  for r, c in zip(ri, ci)
1488
1551
  ]
1489
1552
  ).T
@@ -1501,19 +1564,25 @@ class PS:
1501
1564
  epsilon=epsilon,
1502
1565
  )
1503
1566
  zg = rbf(tg, self.ratio * pg)
1567
+ if method == "spline":
1568
+ # locate valid data
1569
+ ri, ci = np.nonzero(np.isfinite(zg))
1570
+ x, y, z = np.array(
1571
+ [
1572
+ [tg[r, c], pg[r, c], zg[r, c]]
1573
+ for r, c in zip(ri, ci)
1574
+ ]
1575
+ ).T
1576
+ interp = SmoothBivariateSpline(
1577
+ x, self.ratio * y, z, kx=degree, ky=degree
1578
+ )
1579
+ zg = interp(tg, self.ratio * pg)
1504
1580
  except Exception:
1505
1581
  print(
1506
1582
  "Using nearest method in {}".format(
1507
1583
  " ".join(sorted(list(key)))
1508
1584
  )
1509
1585
  )
1510
- zg = griddata(
1511
- np.array(pts),
1512
- data,
1513
- (tg, pg),
1514
- method="nearest",
1515
- rescale=True,
1516
- )
1517
1586
  # experimental
1518
1587
  if gradient:
1519
1588
  grd = np.gradient(zg, self.gridxstep, self.gridystep)
@@ -1674,6 +1743,9 @@ class PS:
1674
1743
  epsilon (int): Adjustable constant for gaussian or multiquadrics
1675
1744
  functions - defaults to approximate average distance between
1676
1745
  nodes (which is a good start).
1746
+ degree (int): Degrees of the bivariate spline. Default is 3.
1747
+ refine (int): Degree of grid refinement. Default 1
1748
+ filter_outliers (bool): Whether to filter outliers. Defaut False.
1677
1749
  out (str or list): Highligt zero-mode lines for given phases.
1678
1750
  overlay (bool): Whether to show assemblage fields. Default True
1679
1751
  high (frozenset or list): Highlight divariant fields identified
@@ -1707,6 +1779,8 @@ class PS:
1707
1779
  cdf = kwargs.get("cdf", False)
1708
1780
  overlay = kwargs.get("overlay", True)
1709
1781
  only = kwargs.get("only", None)
1782
+ filter_outliers = kwargs.get("only", False)
1783
+ degree = kwargs.get("degree", 3)
1710
1784
  refine = kwargs.get("refine", 1)
1711
1785
  method = kwargs.get("method", "rbf")
1712
1786
  rbf_func = kwargs.get("rbf_func", "thin_plate")
@@ -1782,6 +1856,19 @@ class PS:
1782
1856
  x, y = np.array(recs[key]["pts"]).T
1783
1857
  pts = recs[key]["pts"]
1784
1858
  data = recs[key]["data"]
1859
+ # filter outliers
1860
+ if filter_outliers:
1861
+ A = np.c_[np.ones_like(x), x, y, x * y, x**2, y**2]
1862
+ C, _, _, _ = lstsq(A, data)
1863
+ # evaluate it on a grid
1864
+ vals = np.dot(
1865
+ np.c_[np.ones_like(x), x, y, x * y, x**2, y**2], C
1866
+ )
1867
+ err = abs(vals - data)
1868
+ ok = err < err.std()
1869
+ pts = pts[ok, :]
1870
+ x, y = pts.T
1871
+ data = data[ok]
1785
1872
  try:
1786
1873
  if method == "quadratic":
1787
1874
  tgg = tg.flatten()
@@ -1800,22 +1887,76 @@ class PS:
1800
1887
  ],
1801
1888
  C,
1802
1889
  ).reshape(tg.shape)
1803
- else:
1804
- rbf = Rbf(
1805
- x,
1806
- self.ratio * y,
1807
- data,
1808
- function=rbf_func,
1809
- smooth=smooth,
1810
- epsilon=epsilon,
1890
+ elif method == "spline":
1891
+ interp = SmoothBivariateSpline(
1892
+ x, self.ratio * y, data, kx=degree, ky=degree
1811
1893
  )
1812
- zg = rbf(tg, self.ratio * pg)
1894
+ zg = interp(tg, self.ratio * pg)
1895
+ else:
1896
+ with warnings.catch_warnings():
1897
+ warnings.filterwarnings("error")
1898
+ rbf = Rbf(
1899
+ x,
1900
+ self.ratio * y,
1901
+ data,
1902
+ function=rbf_func,
1903
+ smooth=smooth,
1904
+ epsilon=epsilon,
1905
+ )
1906
+ zg = rbf(tg, self.ratio * pg)
1813
1907
  except Exception as e:
1814
1908
  if self.show_errors:
1815
1909
  print(e)
1816
- zg = griddata(
1817
- np.array(pts), data, (tg, pg), method="cubic", rescale=True
1818
- )
1910
+ try:
1911
+ zg = griddata(
1912
+ np.array(pts),
1913
+ data,
1914
+ (tg, pg),
1915
+ method="nearest",
1916
+ rescale=True,
1917
+ )
1918
+ if method == "rbf":
1919
+ # locate valid data
1920
+ ri, ci = np.nonzero(np.isfinite(zg))
1921
+ x, y, z = np.array(
1922
+ [
1923
+ [tg[r, c], pg[r, c], zg[r, c]]
1924
+ for r, c in zip(ri, ci)
1925
+ ]
1926
+ ).T
1927
+ # do Rbf extrapolation
1928
+ with warnings.catch_warnings():
1929
+ warnings.filterwarnings(
1930
+ "ignore", category=LinAlgWarning
1931
+ )
1932
+ rbf = Rbf(
1933
+ x,
1934
+ self.ratio * y,
1935
+ z,
1936
+ function=rbf_func,
1937
+ smooth=smooth,
1938
+ epsilon=epsilon,
1939
+ )
1940
+ zg = rbf(tg, self.ratio * pg)
1941
+ if method == "spline":
1942
+ # locate valid data
1943
+ ri, ci = np.nonzero(np.isfinite(zg))
1944
+ x, y, z = np.array(
1945
+ [
1946
+ [tg[r, c], pg[r, c], zg[r, c]]
1947
+ for r, c in zip(ri, ci)
1948
+ ]
1949
+ ).T
1950
+ interp = SmoothBivariateSpline(
1951
+ x, self.ratio * y, z, kx=degree, ky=degree
1952
+ )
1953
+ zg = interp(tg, self.ratio * pg)
1954
+ except Exception:
1955
+ print(
1956
+ "Using nearest method in {}".format(
1957
+ " ".join(sorted(list(key)))
1958
+ )
1959
+ )
1819
1960
  # ------------
1820
1961
  scx = (tmax - tmin + 2 * self.gridxstep) / zg.shape[1]
1821
1962
  scy = (pmax - pmin + 2 * self.gridystep) / zg.shape[0]
@@ -2123,15 +2264,18 @@ class PS:
2123
2264
  tg, pg = self.xg[slc], self.yg[slc]
2124
2265
  x, y = np.array(recs[key]["pts"]).T
2125
2266
  # Use scaling
2126
- rbf = Rbf(
2127
- x,
2128
- self.ratio * y,
2129
- recs[key]["data"],
2130
- function="linear",
2131
- smooth=smooth,
2132
- )
2133
- zg = rbf(tg, self.ratio * pg)
2134
- gd[self.masks[key]] = zg[self.masks[key][slc]]
2267
+ try:
2268
+ rbf = Rbf(
2269
+ x,
2270
+ self.ratio * y,
2271
+ recs[key]["data"],
2272
+ function="linear",
2273
+ smooth=smooth,
2274
+ )
2275
+ zg = rbf(tg, self.ratio * pg)
2276
+ gd[self.masks[key]] = zg[self.masks[key][slc]]
2277
+ except Exception:
2278
+ pass # pass silently
2135
2279
  return gd
2136
2280
  else:
2137
2281
  print("Not yet gridded...")
@@ -2364,7 +2508,7 @@ class PTPS(PS):
2364
2508
  t, p = splt(step), splp(step)
2365
2509
  key = self.identify(t, p)
2366
2510
  ix = self.get_section_id(t, p)
2367
- if ix is not None:
2511
+ if (ix is not None) and (key is not None):
2368
2512
  r, c = self.grids[ix].get_indexes(t, p)
2369
2513
  calc = None
2370
2514
  if self.grids[ix].status[r, c] == 1:
@@ -2385,6 +2529,8 @@ class PTPS(PS):
2385
2529
  results.append(res[0])
2386
2530
  else:
2387
2531
  err += 1
2532
+ else:
2533
+ err += 1
2388
2534
  if err > 0:
2389
2535
  print("Solution not found on {} points".format(err))
2390
2536
  return PTpath(points, results)
@@ -2888,8 +3034,10 @@ class GridData:
2888
3034
  y (float): y-coordiante of point
2889
3035
 
2890
3036
  """
2891
- c = np.searchsorted(self.xspace, x)
2892
- r = np.searchsorted(self.yspace, y)
3037
+ # c = np.searchsorted(self.xspace, x)
3038
+ c = np.argmin((self.xspace - x) ** 2)
3039
+ # r = np.searchsorted(self.yspace, y)
3040
+ r = np.argmin((self.yspace - y) ** 2)
2893
3041
  return r, c
2894
3042
 
2895
3043
  def contains(self, x, y):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pypsbuilder
3
- Version: 2.3.6
3
+ Version: 2.4.0
4
4
  Summary: THERMOCALC front-end for constructing and analyzing PT pseudosections
5
5
  Home-page: https://github.com/ondrolexa/pypsbuilder
6
6
  Author: Ondrej Lexa
@@ -88,6 +88,12 @@ pypsbuilder is free software: you can redistribute it and/or modify it under the
88
88
  # Changelog
89
89
  All notable pypsbuilder changes.
90
90
 
91
+ ## [2.4.0] - 2023-12-21
92
+ ### Fixed
93
+ - important collect_grid_data bug causing isopleths twisting fixed.
94
+ - collect_ptpath missing field check added
95
+ - remove_grid_data method added
96
+ - isopleths bivariate spline method added
91
97
  ## [2.3.6] - 2023-12-13
92
98
  ### Added
93
99
  - search_composition method added to PTPS to find best estimate for isopleths intersection
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.3.6
2
+ current_version = 2.4.0
3
3
  commit = True
4
4
  tag = False
5
5
 
@@ -5,34 +5,27 @@ from setuptools import setup, find_packages
5
5
 
6
6
  CURRENT_PATH = path.abspath(path.dirname(__file__))
7
7
 
8
- with open(path.join(CURRENT_PATH, 'README.md')) as readme_file:
8
+ with open(path.join(CURRENT_PATH, "README.md")) as readme_file:
9
9
  readme = readme_file.read()
10
10
 
11
- with open(path.join(CURRENT_PATH, 'CHANGELOG.md')) as changelog_file:
11
+ with open(path.join(CURRENT_PATH, "CHANGELOG.md")) as changelog_file:
12
12
  changelog = changelog_file.read()
13
13
 
14
- requirements = [
15
- 'numpy',
16
- 'matplotlib>=3',
17
- 'scipy',
18
- 'networkx',
19
- 'shapely>=2',
20
- 'tqdm'
21
- ]
14
+ requirements = ["numpy", "matplotlib>=3", "scipy", "networkx", "shapely>=2", "tqdm"]
22
15
 
23
16
  setup(
24
- name='pypsbuilder',
25
- version='2.3.6',
17
+ name="pypsbuilder",
18
+ version="2.4.0",
26
19
  description="THERMOCALC front-end for constructing and analyzing PT pseudosections",
27
- long_description=readme + '\n\n' + changelog,
20
+ long_description=readme + "\n\n" + changelog,
28
21
  long_description_content_type="text/markdown",
29
22
  author="Ondrej Lexa",
30
- author_email='lexa.ondrej@gmail.com',
31
- url='https://github.com/ondrolexa/pypsbuilder',
23
+ author_email="lexa.ondrej@gmail.com",
24
+ url="https://github.com/ondrolexa/pypsbuilder",
32
25
  license="MIT",
33
26
  python_requires=">=3.8",
34
27
  packages=find_packages(),
35
- package_data={'pypsbuilder.images': ['*.png']},
28
+ package_data={"pypsbuilder.images": ["*.png"]},
36
29
  entry_points="""
37
30
  [console_scripts]
38
31
  ptbuilder=pypsbuilder.psbuilders:ptbuilder
@@ -46,13 +39,13 @@ setup(
46
39
  """,
47
40
  install_requires=requirements,
48
41
  zip_safe=False,
49
- keywords='pypsbuilder',
42
+ keywords="pypsbuilder",
50
43
  classifiers=[
51
- 'Development Status :: 4 - Beta',
52
- 'Intended Audience :: Science/Research',
53
- 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
54
- 'Programming Language :: Python :: 3 :: Only',
55
- 'Topic :: Scientific/Engineering',
56
- 'Topic :: Utilities'
57
- ]
44
+ "Development Status :: 4 - Beta",
45
+ "Intended Audience :: Science/Research",
46
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
47
+ "Programming Language :: Python :: 3 :: Only",
48
+ "Topic :: Scientific/Engineering",
49
+ "Topic :: Utilities",
50
+ ],
58
51
  )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes