tpmslab 0.3.0__tar.gz → 0.3.4__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 (88) hide show
  1. {tpmslab-0.3.0 → tpmslab-0.3.4}/.zenodo.json +2 -2
  2. {tpmslab-0.3.0 → tpmslab-0.3.4}/CHANGELOG.md +26 -0
  3. {tpmslab-0.3.0 → tpmslab-0.3.4}/CITATION.cff +1 -1
  4. {tpmslab-0.3.0 → tpmslab-0.3.4}/MANIFEST.in +2 -0
  5. {tpmslab-0.3.0 → tpmslab-0.3.4}/PKG-INFO +227 -220
  6. {tpmslab-0.3.0 → tpmslab-0.3.4}/README.md +11 -4
  7. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/release_reference.py +14 -6
  8. tpmslab-0.3.4/docs/PYPI_PUBLISHING.md +47 -0
  9. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/QUICKSTART_EN.md +104 -95
  10. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/QUICKSTART_ZH.md +62 -53
  11. tpmslab-0.3.4/docs/RELEASE_NOTES_v0.3.1.md +7 -0
  12. tpmslab-0.3.4/docs/RELEASE_NOTES_v0.3.3.md +7 -0
  13. tpmslab-0.3.4/docs/RELEASE_NOTES_v0.3.4.md +7 -0
  14. tpmslab-0.3.4/docs/runtime_hashes_0.3.1.json +15 -0
  15. {tpmslab-0.3.0 → tpmslab-0.3.4}/pyproject.toml +1 -1
  16. {tpmslab-0.3.0 → tpmslab-0.3.4}/setup.cfg +4 -4
  17. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/__init__.py +1 -1
  18. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/comsol.py +12 -12
  19. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/comsol_dual.py +1 -1
  20. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/model.py +36 -32
  21. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/multidomain.py +8 -7
  22. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/app.js +30 -30
  23. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/index.html +1 -1
  24. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/volume.py +311 -310
  25. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/web.py +302 -302
  26. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/PKG-INFO +227 -220
  27. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/SOURCES.txt +8 -0
  28. tpmslab-0.3.4/tests/test_calibration_config.py +46 -0
  29. {tpmslab-0.3.0 → tpmslab-0.3.4}/tests/test_core.py +151 -117
  30. tpmslab-0.3.4/tests/test_english_runtime.py +38 -0
  31. tpmslab-0.3.4/tests/test_report_version.py +7 -0
  32. {tpmslab-0.3.0 → tpmslab-0.3.4}/LICENSE +0 -0
  33. {tpmslab-0.3.0 → tpmslab-0.3.4}/THIRD_PARTY_NOTICES.md +0 -0
  34. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/density_study.py +0 -0
  35. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/revision_study.py +0 -0
  36. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/run_benchmarks.py +0 -0
  37. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/verify_comsol.py +0 -0
  38. {tpmslab-0.3.0 → tpmslab-0.3.4}/benchmarks/verify_solid_fluid.py +0 -0
  39. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/METHOD.md +0 -0
  40. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/RELEASE_NOTES_v0.3.0.md +0 -0
  41. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/SOLID_FLUID.md +0 -0
  42. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/VALIDATION.md +0 -0
  43. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/VERIFICATION.md +0 -0
  44. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/VERSION_0.3.0.md +0 -0
  45. {tpmslab-0.3.0 → tpmslab-0.3.4}/docs/lattgen_reference.m +0 -0
  46. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/graded_gyroid.json +0 -0
  47. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/graded_gyroid.py +0 -0
  48. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/gyroid_graded.json +0 -0
  49. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/primitive_graded.json +0 -0
  50. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/solid_fluid.json +0 -0
  51. {tpmslab-0.3.0 → tpmslab-0.3.4}/examples/solid_fluid_api.py +0 -0
  52. {tpmslab-0.3.0 → tpmslab-0.3.4}/licenses/LattGen-MIT.txt +0 -0
  53. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/__main__.py +0 -0
  54. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/cli.py +0 -0
  55. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/comsol_audit.py +0 -0
  56. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/families.json +0 -0
  57. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/io.py +0 -0
  58. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/py.typed +0 -0
  59. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/quality.py +0 -0
  60. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/style.css +0 -0
  61. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/vendor/OrbitControls.js +0 -0
  62. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/vendor/THREE-LICENSE.txt +0 -0
  63. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/static/vendor/three.module.js +0 -0
  64. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab/verification.py +0 -0
  65. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/dependency_links.txt +0 -0
  66. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/entry_points.txt +0 -0
  67. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/requires.txt +0 -0
  68. {tpmslab-0.3.0 → tpmslab-0.3.4}/src/tpmslab.egg-info/top_level.txt +0 -0
  69. {tpmslab-0.3.0 → tpmslab-0.3.4}/tests/test_multidomain.py +0 -0
  70. {tpmslab-0.3.0 → tpmslab-0.3.4}/tests/test_package.py +0 -0
  71. {tpmslab-0.3.0 → tpmslab-0.3.4}/tests/test_verification.py +0 -0
  72. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/README.md +0 -0
  73. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/checksums.json +0 -0
  74. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/code_commit.json +0 -0
  75. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/density/density_results.json +0 -0
  76. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/environment.json +0 -0
  77. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/existing_Diamond_audit/audit.json +0 -0
  78. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/existing_Gyroid_audit/audit.json +0 -0
  79. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/existing_Primitive_Schwartz_audit/audit.json +0 -0
  80. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/historical_windows_checksums.json +0 -0
  81. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/performance_summary.csv +0 -0
  82. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/provenance.json +0 -0
  83. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/quality_summary.csv +0 -0
  84. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/reference_configs/Diamond.json +0 -0
  85. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/reference_configs/Gyroid.json +0 -0
  86. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/reference_configs/Primitive_Schwartz.json +0 -0
  87. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/refinement_summary.csv +0 -0
  88. {tpmslab-0.3.0 → tpmslab-0.3.4}/validation/summary.json +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
- "title": "TPMS Lab v0.3.0",
3
- "version": "0.3.0",
2
+ "title": "TPMS Lab v0.3.1",
3
+ "version": "0.3.1",
4
4
  "upload_type": "software",
5
5
  "description": "Python package for graded complementary solid and pore-fluid meshes with shared interfaces, connected-domain metadata and optional verified COMSOL workflows. Source, tests, examples and numerical validation records are included.",
6
6
  "creators": [
@@ -1,3 +1,29 @@
1
+ # 0.3.4
2
+
3
+ - Add configurable density-calibration sampling via `m_cal` (default 56).
4
+
5
+ - Remove fixed upper limits on per-cell resolution and per-axis cell counts.
6
+ - Use an uncapped integer input in the browser interface.
7
+ - Preserve lower bounds, integer validation and numerical algorithms.
8
+
9
+ # 0.3.3
10
+
11
+ - Set the per-cell resolution maximum to 48 in Python and the browser slider.
12
+ - Retain removal of the total background-grid cap.
13
+ - Synchronize documentation and add boundary regression checks.
14
+ - Report the running generator version instead of a stale literal; geometry is unchanged.
15
+
16
+ # 0.3.2
17
+
18
+ - Remove the fixed 200,000-background-voxel limit from configuration validation.
19
+ - Retain per-axis cell-count and per-cell resolution validation.
20
+ - Add configuration regression coverage for grids above the former limit.
21
+
22
+ # 0.3.1
23
+
24
+ - Use English for Python progress callbacks, exceptions, API messages and exported warnings.
25
+ - Update generator metadata; preserve numerical algorithms and historical validation records.
26
+
1
27
  # Changelog
2
28
 
3
29
  ## 0.3.0
@@ -2,7 +2,7 @@ cff-version: 1.2.0
2
2
  message: "Please cite the exact software version used."
3
3
  type: software
4
4
  title: "TPMS Lab"
5
- version: "0.3.0"
5
+ version: "0.3.1"
6
6
  authors:
7
7
  - family-names: Chen
8
8
  given-names: Xin
@@ -10,3 +10,5 @@ prune paper
10
10
 
11
11
  recursive-include validation *.json *.csv *.md
12
12
  include CITATION.cff .zenodo.json
13
+
14
+ include docs/runtime_hashes_0.3.1.json
@@ -1,220 +1,227 @@
1
- Metadata-Version: 2.4
2
- Name: tpmslab
3
- Version: 0.3.0
4
- Summary: Direct conforming volume meshes for graded implicit TPMS lattices
5
- License-Expression: MIT
6
- Keywords: TPMS,finite-element,meshing,graded-lattice,COMSOL
7
- Classifier: Development Status :: 4 - Beta
8
- Classifier: Intended Audience :: Science/Research
9
- Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.11
11
- Classifier: Programming Language :: Python :: 3.12
12
- Classifier: Topic :: Scientific/Engineering
13
- Requires-Python: >=3.11
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- License-File: licenses/LattGen-MIT.txt
17
- Requires-Dist: numpy<3,>=1.26
18
- Requires-Dist: scipy<2,>=1.11
19
- Requires-Dist: trimesh<6,>=4
20
- Provides-Extra: web
21
- Requires-Dist: flask<4,>=3.1; extra == "web"
22
- Provides-Extra: test
23
- Requires-Dist: pytest<10,>=8; extra == "test"
24
- Requires-Dist: flask<4,>=3.1; extra == "test"
25
- Provides-Extra: dev
26
- Requires-Dist: pytest<10,>=8; extra == "dev"
27
- Requires-Dist: flask<4,>=3.1; extra == "dev"
28
- Requires-Dist: build>=1.2; extra == "dev"
29
- Requires-Dist: twine>=6; extra == "dev"
30
- Requires-Dist: ruff>=0.12; extra == "dev"
31
- Dynamic: license-file
32
-
33
- # TPMS Lab
34
-
35
- [English quick start](docs/QUICKSTART_EN.md) | [中文使用说明](docs/QUICKSTART_ZH.md)
36
-
37
- An installable Python toolkit for graded TPMS-derived **solid and complementary pore-fluid volume meshes**.
38
- Version 0.3.0. This repository is public. Source, tests, examples and versioned
39
- reproducibility records are distributed together. Use an exact commit or the
40
- `v0.3.0` tag, rather than a changing `main`, for reproducible work.
41
-
42
- [Version and installation details](docs/VERSION_0.3.0.md) |
43
- [GitHub releases](https://github.com/Slight1211/TPMSLab/releases)
44
-
45
- A public source repository, a GitHub Release, a Zenodo archive and a PyPI upload
46
- are separate records. Cite a DOI only after the archive is actually published.
47
- No PyPI release has been made.
48
-
49
- ## Install
50
-
51
- Use Python 3.11 or newer. From this source directory:
52
-
53
- ```sh
54
- python -m pip install .
55
- # Optional local browser interface
56
- python -m pip install ".[web]"
57
- ```
58
-
59
- Build a wheel locally with `python -m build` (requires the `build` package). The core does not require Flask, COMSOL, MATLAB,
60
- OpenCascade, a CAD import licence or a graphics display. COMSOL model creation
61
- and solving require a separate compatible COMSOL installation and licence.
62
-
63
- ## Python API
64
-
65
- ```python
66
- from tpmslab import Config, generate, save_model
67
-
68
- mesh = generate(Config(family="Gyroid", resolution=16,
69
- density_start=0.25, density_end=0.50,
70
- gradient="linear", quality_strategy="quality_fan"))
71
- print(mesh["report"]["actual_density"])
72
- save_model(mesh, "gyroid_result") # destination must not exist
73
- ```
74
-
75
- `generate` also accepts a dictionary. `list_families()` returns formula metadata.
76
- Coordinates are millimetres; array connectivity is zero-based. The returned
77
- mapping contains `points`, `tetra`, `boundary`, `boundary_ids`, `domains`,
78
- `surface` (a trimesh object) and `report`. This API may change before
79
- 1.0. Configuration objects normalize sequence fields to immutable tuples.
80
-
81
- ## Command line
82
-
83
- ```sh
84
- tpmslab init-config params.json
85
- tpmslab generate --config params.json --out result
86
- tpmslab families
87
- tpmslab web --output local_models
88
- # Requires licensed COMSOL on Windows:
89
- tpmslab generate --config params.json --out solved_result --solve
90
- ```
91
-
92
- Export includes `mesh.nas` (CTETRA volume elements and labelled boundary
93
- triangles), `mesh.npz`, `preview.stl`, parameters, diagnostics, environment and a
94
- SHA-256 manifest. Existing output directories are rejected. A failed export
95
- can leave a partial directory; choose a new destination after addressing the
96
- error. STL is only a boundary preview. The optional bridge imports the volume
97
- mesh, creates an MPH and reopens it to check the embedded element count.
98
- Set `COMSOL_BIN` to the folder containing `comsolbatch.exe` and
99
- `comsolcompile.exe` if automatic Windows detection fails.
100
-
101
- ## Method and limits
102
-
103
- The library samples trigonometric level-set approximations, calibrates density
104
- against a 56 cubed periodic-cell sample and clips a six-tetrahedra-per-voxel
105
- background mesh. Sheet structures use two separate interpolated inequalities;
106
- they do not have constant physical wall thickness. Shared intersections and
107
- canonical face diagonals form a conforming tetrahedral volume mesh. Material
108
- volume, boundary closure, face incidence and orientation are checked before
109
- export. The result is a piecewise-planar solid domain, **not editable smooth
110
- STEP/NURBS CAD**. Mesh generation can reject difficult parameter combinations.
111
-
112
- `quality_fan` compares the original pulling fan with an interior-centroid fan
113
- for poor cut cells and accepts only a better worst mean-ratio quality while
114
- preserving boundary triangles and volume. This increases element count and can
115
- reduce median quality. It provides no angle bound, global optimization or
116
- finite-element convergence guarantee. `pulling` retains the baseline method.
117
-
118
- There are 29 reference labels (27 distinct expressions, with aliases), eight
119
- density profiles, sheet and two network modes, phase controls, and a restricted
120
- custom-formula parser. Built-in formulas are adapted from LattGen; see
121
- `THIRD_PARTY_NOTICES.md`. They are not claimed as new TPMS families or exact
122
- minimal surfaces. Density targets are periodic-cell calibrations, not guaranteed
123
- finite-specimen volume fractions. Current bounds: box domains, 1–6 cells per
124
- axis, 8–64 samples per cell and at most 200,000 background voxels. The built-in
125
- static demo is limited to 180,000 tetrahedra and connected material. Top/bottom
126
- contact, constraints and convergence must be checked for a research model.
127
-
128
- ## Development and reproduction
129
-
130
- ```sh
131
- python -m pip install ".[dev]"
132
- python -m pytest
133
- python -m ruff check src tests benchmarks
134
- python -m build
135
- python -m twine check dist/*
136
- python benchmarks/run_benchmarks.py --out benchmark_results
137
- ```
138
-
139
- See `docs/METHOD.md`, `docs/SOLID_FLUID.md` and `docs/VALIDATION.md`.
140
- The CI matrix is configured for Windows/Linux and Python 3.11/3.12; configuring
141
- CI does not mean those jobs have run. The measured local environment is Windows
142
- and Python 3.12. Optional COMSOL validation is separate from the open test suite.
143
-
144
- ## Licence and attribution
145
-
146
- MIT. See `LICENSE`, `THIRD_PARTY_NOTICES.md` and `licenses/` for the
147
- package licence and third-party formula and browser-library notices.
148
-
149
- ## Solid and pore-fluid domains (0.3)
150
-
151
- ```python
152
- from tpmslab import Config, generate, save_model
153
- from tpmslab.comsol import build_mph
154
-
155
- mesh = generate(Config(domain_mode="solid_fluid", family="Gyroid", resolution=12))
156
- folder = save_model(mesh, "solid_fluid_gyroid")
157
- # Optional, licensed COMSOL: verifies selections, each domain volume and saved mesh.
158
- build_mph(folder, mesh["report"], solve=False)
159
- # solve=True instead runs the fixed-wall pore-flow demonstration described below.
160
- ```
161
-
162
- In the browser, select **固体 + 孔隙流体(共享界面)** under **计算域**.
163
- The phase selector switches the preview between solid and pore fluid.
164
- The default remains solid-only for compatibility. The JSON/CLI parameter is
165
- `"domain_mode": "solid_fluid"`.
166
-
167
- Both phases partition the same bounding box. They use shared cut-edge vertices
168
- and identical interface triangles. For sheet structures the two complementary
169
- inequalities are meshed separately, allowing disconnected labyrinths to remain
170
- separate domains. Connectivity is determined by shared tetrahedron faces.
171
- The combined mesh is checked for positive volumes, opposite adjacent-face
172
- orientation, complete interface matching and total volume conservation.
173
-
174
- `phase_ids` is one value per tetrahedron (1=solid, 2=fluid); `domains` identifies
175
- individual connected domains. `interface` is oriented out of the solid.
176
- `boundary` includes the outer boundary AND the solid-fluid interface once.
177
- Additional arrays are `solid_boundary` and `fluid_boundary`. `surface` and
178
- `preview.stl` continue to show the solid; `fluid_surface` and `fluid_preview.stl`
179
- show the pore fluid. The NPZ retains all phase and boundary arrays. `domains.json`
180
- records domain-to-phase and boundary-name mappings. Report `volume_mm3` remains
181
- solid volume; `total_mesh_volume_mm3` includes both phases.
182
-
183
- NASTRAN PSOLID property IDs are `100 + domain_id`. Boundary PIDs 2–7 are solid
184
- xmin/xmax/ymin/ymax/zmin/zmax; 12–17 are the corresponding fluid faces; PID 8
185
- is the shared interface. PIDs are NOT COMSOL entity numbers. The bridge resolves
186
- imported PID selections and creates `solid_domains`, `fluid_domains`, individual
187
- connected-domain selections, `solid_fluid_interface`, and the nonempty exterior
188
- face selections. It assigns demonstration solid/fluid materials separately.
189
- An unsolved export requires no CFD physics interface and is a starting point
190
- for the user's physics setup.
191
-
192
- For dual-domain models, `solve=True` runs stationary **creeping flow only in the
193
- fluid**, with fixed no-slip solid walls, pressure 0.01 Pa at fluid_zmin and 0 Pa
194
- at fluid_zmax, and no-slip on other fluid exterior faces. Density is 1000 kg/m³
195
- and viscosity 0.001 Pa·s. Every fluid component must reach both Z faces; otherwise
196
- the automatic demo is refused while unsolved export remains available. This
197
- requires the corresponding COMSOL physics licence. It reports inlet/outlet
198
- flow and relative imbalance (the smoke-test threshold is 5%, not an accuracy
199
- claim). It is **not deformation-coupled FSI**, not conjugate heat transfer, and
200
- not a validated permeability prediction. Boundary layers, external reservoirs,
201
- mesh convergence and realistic boundary conditions remain user responsibilities.
202
- See `docs/SOLID_FLUID.md` for validation and reproducibility.
203
-
204
- ## Solid–fluid quick start
205
-
206
- Run [the Python example](examples/solid_fluid_api.py). Installation, exported
207
- files and optional COMSOL calculations are documented in the
208
- [English quick start](docs/QUICKSTART_EN.md) and
209
- [中文使用说明](docs/QUICKSTART_ZH.md).
210
-
211
- ## Independent verification
212
-
213
- See [Verification and reproducible studies](docs/VERIFICATION.md) for density realization, independent interface incidence and area audits, channel-wise COMSOL flux checks, and refinement/performance scripts. `quality_fan` is optional local worst-element mitigation, not a general quality or solver-speed guarantee.
214
-
215
- ## Reproducibility records
216
-
217
- See [validation/README.md](validation/README.md) for the numerical records,
218
- configuration provenance and reproduction commands corresponding to the verified
219
- workflow. The package provides mesh-defined simulation domains, not smooth CAD
220
- reconstruction or coupled FSI.
1
+ Metadata-Version: 2.4
2
+ Name: tpmslab
3
+ Version: 0.3.4
4
+ Summary: Direct conforming volume meshes for graded implicit TPMS lattices
5
+ License-Expression: MIT
6
+ Keywords: TPMS,finite-element,meshing,graded-lattice,COMSOL
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Scientific/Engineering
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ License-File: licenses/LattGen-MIT.txt
17
+ Requires-Dist: numpy<3,>=1.26
18
+ Requires-Dist: scipy<2,>=1.11
19
+ Requires-Dist: trimesh<6,>=4
20
+ Provides-Extra: web
21
+ Requires-Dist: flask<4,>=3.1; extra == "web"
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest<10,>=8; extra == "test"
24
+ Requires-Dist: flask<4,>=3.1; extra == "test"
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest<10,>=8; extra == "dev"
27
+ Requires-Dist: flask<4,>=3.1; extra == "dev"
28
+ Requires-Dist: build>=1.2; extra == "dev"
29
+ Requires-Dist: twine>=6; extra == "dev"
30
+ Requires-Dist: ruff>=0.12; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # TPMS Lab
34
+
35
+ [English quick start](docs/QUICKSTART_EN.md) | [中文使用说明](docs/QUICKSTART_ZH.md)
36
+
37
+ An installable Python toolkit for graded TPMS-derived **solid and complementary pore-fluid volume meshes**.
38
+ The current source revision is 0.3.4 (no fixed resolution or cell-count upper limits). The published
39
+ PyPI baseline is 0.3.0. This repository is public. Source, tests, examples and versioned
40
+ reproducibility records are distributed together. Use an exact commit or the
41
+ `v0.3.0` tag, rather than a changing `main`, for reproducible work.
42
+
43
+ [Version and installation details](docs/VERSION_0.3.0.md) |
44
+ [GitHub releases](https://github.com/Slight1211/TPMSLab/releases)
45
+
46
+ A public source repository, a GitHub Release, a Zenodo archive and a PyPI upload
47
+ are separate records. Cite a DOI only after the archive is actually published.
48
+ Version 0.3.0 is available on [PyPI](https://pypi.org/project/tpmslab/0.3.0/).
49
+ Version 0.3.4 is a source/local-wheel patch until a separate release is published.
50
+ See [patch notes](docs/RELEASE_NOTES_v0.3.4.md).
51
+
52
+ ## Install
53
+
54
+ Use Python 3.11 or newer. From this source directory:
55
+
56
+ ```sh
57
+ python -m pip install .
58
+ # Optional local browser interface
59
+ python -m pip install ".[web]"
60
+ ```
61
+
62
+ Build a wheel locally with `python -m build` (requires the `build` package). The core does not require Flask, COMSOL, MATLAB,
63
+ OpenCascade, a CAD import licence or a graphics display. COMSOL model creation
64
+ and solving require a separate compatible COMSOL installation and licence.
65
+
66
+ ## Python API
67
+
68
+ ```python
69
+ from tpmslab import Config, generate, save_model
70
+
71
+ mesh = generate(Config(family="Gyroid", resolution=16,
72
+ density_start=0.25, density_end=0.50,
73
+ gradient="linear", quality_strategy="quality_fan"))
74
+ print(mesh["report"]["actual_density"])
75
+ save_model(mesh, "gyroid_result") # destination must not exist
76
+ ```
77
+
78
+ `generate` also accepts a dictionary. `list_families()` returns formula metadata.
79
+ Coordinates are millimetres; array connectivity is zero-based. The returned
80
+ mapping contains `points`, `tetra`, `boundary`, `boundary_ids`, `domains`,
81
+ `surface` (a trimesh object) and `report`. This API may change before
82
+ 1.0. Configuration objects normalize sequence fields to immutable tuples.
83
+
84
+ ## Command line
85
+
86
+ ```sh
87
+ tpmslab init-config params.json
88
+ tpmslab generate --config params.json --out result
89
+ tpmslab families
90
+ tpmslab web --output local_models
91
+ # Requires licensed COMSOL on Windows:
92
+ tpmslab generate --config params.json --out solved_result --solve
93
+ ```
94
+
95
+ Export includes `mesh.nas` (CTETRA volume elements and labelled boundary
96
+ triangles), `mesh.npz`, `preview.stl`, parameters, diagnostics, environment and a
97
+ SHA-256 manifest. Existing output directories are rejected. A failed export
98
+ can leave a partial directory; choose a new destination after addressing the
99
+ error. STL is only a boundary preview. The optional bridge imports the volume
100
+ mesh, creates an MPH and reopens it to check the embedded element count.
101
+ Set `COMSOL_BIN` to the folder containing `comsolbatch.exe` and
102
+ `comsolcompile.exe` if automatic Windows detection fails.
103
+
104
+ ## Method and limits
105
+
106
+ The library samples trigonometric level-set approximations, calibrates density
107
+ against a 56 cubed periodic-cell sample and clips a six-tetrahedra-per-voxel
108
+ background mesh. Sheet structures use two separate interpolated inequalities;
109
+ they do not have constant physical wall thickness. Shared intersections and
110
+ canonical face diagonals form a conforming tetrahedral volume mesh. Material
111
+ volume, boundary closure, face incidence and orientation are checked before
112
+ export. The result is a piecewise-planar solid domain, **not editable smooth
113
+ STEP/NURBS CAD**. Mesh generation can reject difficult parameter combinations.
114
+
115
+ `quality_fan` compares the original pulling fan with an interior-centroid fan
116
+ for poor cut cells and accepts only a better worst mean-ratio quality while
117
+ preserving boundary triangles and volume. This increases element count and can
118
+ reduce median quality. It provides no angle bound, global optimization or
119
+ finite-element convergence guarantee. `pulling` retains the baseline method.
120
+
121
+ There are 29 reference labels (27 distinct expressions, with aliases), eight
122
+ density profiles, sheet and two network modes, phase controls, and a restricted
123
+ custom-formula parser. Built-in formulas are adapted from LattGen; see
124
+ `THIRD_PARTY_NOTICES.md`. They are not claimed as new TPMS families or exact
125
+ minimal surfaces. Density targets are periodic-cell calibrations, not guaranteed
126
+ finite-specimen volume fractions. Current bounds: box domains, positive integer cell counts per
127
+ axis and at least 8 samples per cell, with no fixed upper limits. There is no fixed background-voxel count cap; feasible grid sizes depend on available memory. The built-in
128
+ static demo is limited to 180,000 tetrahedra and connected material. Top/bottom
129
+ contact, constraints and convergence must be checked for a research model.
130
+
131
+ ## Development and reproduction
132
+
133
+ ```sh
134
+ python -m pip install ".[dev]"
135
+ python -m pytest
136
+ python -m ruff check src tests benchmarks
137
+ python -m build
138
+ python -m twine check dist/*
139
+ python benchmarks/run_benchmarks.py --out benchmark_results
140
+ ```
141
+
142
+ See `docs/METHOD.md`, `docs/SOLID_FLUID.md` and `docs/VALIDATION.md`.
143
+ The CI matrix is configured for Windows/Linux and Python 3.11/3.12; configuring
144
+ CI does not mean those jobs have run. The measured local environment is Windows
145
+ and Python 3.12. Optional COMSOL validation is separate from the open test suite.
146
+
147
+ ## Licence and attribution
148
+
149
+ MIT. See `LICENSE`, `THIRD_PARTY_NOTICES.md` and `licenses/` for the
150
+ package licence and third-party formula and browser-library notices.
151
+
152
+ ## Solid and pore-fluid domains (0.3)
153
+
154
+ ```python
155
+ from tpmslab import Config, generate, save_model
156
+ from tpmslab.comsol import build_mph
157
+
158
+ mesh = generate(Config(domain_mode="solid_fluid", family="Gyroid", resolution=12))
159
+ folder = save_model(mesh, "solid_fluid_gyroid")
160
+ # Optional, licensed COMSOL: verifies selections, each domain volume and saved mesh.
161
+ build_mph(folder, mesh["report"], solve=False)
162
+ # solve=True instead runs the fixed-wall pore-flow demonstration described below.
163
+ ```
164
+
165
+ In the browser, select **固体 + 孔隙流体(共享界面)** under **计算域**.
166
+ The phase selector switches the preview between solid and pore fluid.
167
+ The default remains solid-only for compatibility. The JSON/CLI parameter is
168
+ `"domain_mode": "solid_fluid"`.
169
+
170
+ Both phases partition the same bounding box. They use shared cut-edge vertices
171
+ and identical interface triangles. For sheet structures the two complementary
172
+ inequalities are meshed separately, allowing disconnected labyrinths to remain
173
+ separate domains. Connectivity is determined by shared tetrahedron faces.
174
+ The combined mesh is checked for positive volumes, opposite adjacent-face
175
+ orientation, complete interface matching and total volume conservation.
176
+
177
+ `phase_ids` is one value per tetrahedron (1=solid, 2=fluid); `domains` identifies
178
+ individual connected domains. `interface` is oriented out of the solid.
179
+ `boundary` includes the outer boundary AND the solid-fluid interface once.
180
+ Additional arrays are `solid_boundary` and `fluid_boundary`. `surface` and
181
+ `preview.stl` continue to show the solid; `fluid_surface` and `fluid_preview.stl`
182
+ show the pore fluid. The NPZ retains all phase and boundary arrays. `domains.json`
183
+ records domain-to-phase and boundary-name mappings. Report `volume_mm3` remains
184
+ solid volume; `total_mesh_volume_mm3` includes both phases.
185
+
186
+ NASTRAN PSOLID property IDs are `100 + domain_id`. Boundary PIDs 2–7 are solid
187
+ xmin/xmax/ymin/ymax/zmin/zmax; 12–17 are the corresponding fluid faces; PID 8
188
+ is the shared interface. PIDs are NOT COMSOL entity numbers. The bridge resolves
189
+ imported PID selections and creates `solid_domains`, `fluid_domains`, individual
190
+ connected-domain selections, `solid_fluid_interface`, and the nonempty exterior
191
+ face selections. It assigns demonstration solid/fluid materials separately.
192
+ An unsolved export requires no CFD physics interface and is a starting point
193
+ for the user's physics setup.
194
+
195
+ For dual-domain models, `solve=True` runs stationary **creeping flow only in the
196
+ fluid**, with fixed no-slip solid walls, pressure 0.01 Pa at fluid_zmin and 0 Pa
197
+ at fluid_zmax, and no-slip on other fluid exterior faces. Density is 1000 kg/m³
198
+ and viscosity 0.001 Pa·s. Every fluid component must reach both Z faces; otherwise
199
+ the automatic demo is refused while unsolved export remains available. This
200
+ requires the corresponding COMSOL physics licence. It reports inlet/outlet
201
+ flow and relative imbalance (the smoke-test threshold is 5%, not an accuracy
202
+ claim). It is **not deformation-coupled FSI**, not conjugate heat transfer, and
203
+ not a validated permeability prediction. Boundary layers, external reservoirs,
204
+ mesh convergence and realistic boundary conditions remain user responsibilities.
205
+ See `docs/SOLID_FLUID.md` for validation and reproducibility.
206
+
207
+ ## Solid–fluid quick start
208
+
209
+ Run [the Python example](examples/solid_fluid_api.py). Installation, exported
210
+ files and optional COMSOL calculations are documented in the
211
+ [English quick start](docs/QUICKSTART_EN.md) and
212
+ [中文使用说明](docs/QUICKSTART_ZH.md).
213
+
214
+ ## Independent verification
215
+
216
+ See [Verification and reproducible studies](docs/VERIFICATION.md) for density realization, independent interface incidence and area audits, channel-wise COMSOL flux checks, and refinement/performance scripts. `quality_fan` is optional local worst-element mitigation, not a general quality or solver-speed guarantee.
217
+
218
+ ## Reproducibility records
219
+
220
+ See [validation/README.md](validation/README.md) for the numerical records,
221
+ configuration provenance and reproduction commands corresponding to the verified
222
+ workflow. The package provides mesh-defined simulation domains, not smooth CAD
223
+ reconstruction or coupled FSI.
224
+
225
+ ### Density calibration sampling
226
+
227
+ `Config(m_cal=80)` sets the number of midpoint samples per axis used to calibrate density to threshold (80 cubed samples). The default is 56; values must be integers of at least 8, with no fixed upper bound. This parameter is independent of mesh `resolution`. JSON configurations use the same `m_cal` key; older configurations default to 56. Exported configuration records include the selected value. Increasing it changes density calibration and can change the generated geometry. Historical validation used 56.
@@ -3,7 +3,8 @@
3
3
  [English quick start](docs/QUICKSTART_EN.md) | [中文使用说明](docs/QUICKSTART_ZH.md)
4
4
 
5
5
  An installable Python toolkit for graded TPMS-derived **solid and complementary pore-fluid volume meshes**.
6
- Version 0.3.0. This repository is public. Source, tests, examples and versioned
6
+ The current source revision is 0.3.4 (no fixed resolution or cell-count upper limits). The published
7
+ PyPI baseline is 0.3.0. This repository is public. Source, tests, examples and versioned
7
8
  reproducibility records are distributed together. Use an exact commit or the
8
9
  `v0.3.0` tag, rather than a changing `main`, for reproducible work.
9
10
 
@@ -12,7 +13,9 @@ reproducibility records are distributed together. Use an exact commit or the
12
13
 
13
14
  A public source repository, a GitHub Release, a Zenodo archive and a PyPI upload
14
15
  are separate records. Cite a DOI only after the archive is actually published.
15
- No PyPI release has been made.
16
+ Version 0.3.0 is available on [PyPI](https://pypi.org/project/tpmslab/0.3.0/).
17
+ Version 0.3.4 is a source/local-wheel patch until a separate release is published.
18
+ See [patch notes](docs/RELEASE_NOTES_v0.3.4.md).
16
19
 
17
20
  ## Install
18
21
 
@@ -88,8 +91,8 @@ density profiles, sheet and two network modes, phase controls, and a restricted
88
91
  custom-formula parser. Built-in formulas are adapted from LattGen; see
89
92
  `THIRD_PARTY_NOTICES.md`. They are not claimed as new TPMS families or exact
90
93
  minimal surfaces. Density targets are periodic-cell calibrations, not guaranteed
91
- finite-specimen volume fractions. Current bounds: box domains, 1–6 cells per
92
- axis, 8–64 samples per cell and at most 200,000 background voxels. The built-in
94
+ finite-specimen volume fractions. Current bounds: box domains, positive integer cell counts per
95
+ axis and at least 8 samples per cell, with no fixed upper limits. There is no fixed background-voxel count cap; feasible grid sizes depend on available memory. The built-in
93
96
  static demo is limited to 180,000 tetrahedra and connected material. Top/bottom
94
97
  contact, constraints and convergence must be checked for a research model.
95
98
 
@@ -186,3 +189,7 @@ See [validation/README.md](validation/README.md) for the numerical records,
186
189
  configuration provenance and reproduction commands corresponding to the verified
187
190
  workflow. The package provides mesh-defined simulation domains, not smooth CAD
188
191
  reconstruction or coupled FSI.
192
+
193
+ ### Density calibration sampling
194
+
195
+ `Config(m_cal=80)` sets the number of midpoint samples per axis used to calibrate density to threshold (80 cubed samples). The default is 56; values must be integers of at least 8, with no fixed upper bound. This parameter is independent of mesh `resolution`. JSON configurations use the same `m_cal` key; older configurations default to 56. Exported configuration records include the selected value. Increasing it changes density calibration and can change the generated geometry. Historical validation used 56.
@@ -34,13 +34,21 @@ def main():
34
34
  installed = Path(tpmslab.__file__).resolve().parent
35
35
  if installed == src / "src" / "tpmslab":
36
36
  raise RuntimeError("Install the wheel first; this check must use the installed package")
37
- assert importlib.metadata.version("tpmslab") == "0.3.0"
37
+ installed_version = importlib.metadata.version("tpmslab")
38
+ assert installed_version in {"0.3.0", "0.3.1"}
38
39
  provenance = json.loads((src / "validation/provenance.json").read_text(encoding="utf8"))
39
- for record in provenance["algorithm_files_unchanged"]:
40
- original = src / record["path"]
41
- packaged = installed / Path(record["path"]).name
42
- assert digest(original) == record["git_lf_sha256"], record["path"]
43
- assert digest(packaged) == record["git_lf_sha256"], packaged
40
+ if installed_version == "0.3.0":
41
+ for record in provenance["algorithm_files_unchanged"]:
42
+ original = src / record["path"]
43
+ packaged = installed / Path(record["path"]).name
44
+ assert digest(original) == record["git_lf_sha256"], record["path"]
45
+ assert digest(packaged) == record["git_lf_sha256"], packaged
46
+ else:
47
+ # Localized messages change source bytes; preserve the historical manifest.
48
+ hashes = json.loads((src / "docs/runtime_hashes_0.3.1.json").read_text())
49
+ for filename, expected in hashes.items():
50
+ assert digest(src / "src/tpmslab" / filename) == expected, filename
51
+ assert digest(installed / filename) == expected, filename
44
52
  checksums = json.loads((src / "validation/checksums.json").read_text(encoding="utf8"))
45
53
  for relative, expected in checksums.items():
46
54
  assert digest(src / "validation" / relative) == expected, relative