metafold 0.12.dev11__tar.gz → 0.12.1__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.
- {metafold-0.12.dev11 → metafold-0.12.1}/PKG-INFO +3 -3
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/simulation/compression_simulation.py +57 -40
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold.egg-info/PKG-INFO +3 -3
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold.egg-info/requires.txt +1 -1
- {metafold-0.12.dev11 → metafold-0.12.1}/pyproject.toml +5 -5
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_compression_simulation.py +7 -4
- {metafold-0.12.dev11 → metafold-0.12.1}/LICENSE +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/README.md +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/__init__.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/api.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/assets.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/auth.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/client.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/exceptions.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/jobs.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/materials.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/projects.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/simulation/__init__.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/simulation/compression_experiment.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/simulation/run_experiment.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/utils.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold/workflows.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold.egg-info/SOURCES.txt +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold.egg-info/dependency_links.txt +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/metafold.egg-info/top_level.txt +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/setup.cfg +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_assets.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_compession_experiment.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_jobs.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_materials.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_projects.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_run_experiment.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_shear_simulation.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_utils.py +0 -0
- {metafold-0.12.dev11 → metafold-0.12.1}/tests/test_workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metafold
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Metafold SDK for Python
|
|
5
5
|
Author-email: Metafold 3D <info@metafold3d.com>
|
|
6
6
|
License: Copyright 2024 Metafold 3D
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
26
26
|
Classifier: Topic :: Scientific/Engineering
|
|
27
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: attrs~=23.2
|
|
@@ -38,7 +38,7 @@ Requires-Dist: dotenv>=0.9.9; extra == "simulation"
|
|
|
38
38
|
Requires-Dist: pandas>=2.3.3; extra == "simulation"
|
|
39
39
|
Requires-Dist: plyfile>=1.1.3; extra == "simulation"
|
|
40
40
|
Requires-Dist: pyyaml>=6.0.3; extra == "simulation"
|
|
41
|
-
Requires-Dist: simulation-configurator==0.
|
|
41
|
+
Requires-Dist: simulation-configurator==0.2.*; extra == "simulation"
|
|
42
42
|
Requires-Dist: tables>=3.11.1; extra == "simulation"
|
|
43
43
|
Dynamic: license-file
|
|
44
44
|
|
|
@@ -8,7 +8,7 @@ import uuid
|
|
|
8
8
|
|
|
9
9
|
from requests import HTTPError
|
|
10
10
|
import yaml
|
|
11
|
-
from dotenv import load_dotenv
|
|
11
|
+
from dotenv import load_dotenv # type: ignore
|
|
12
12
|
from metafold import MetafoldClient
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from numpy.typing import DTypeLike
|
|
@@ -353,6 +353,8 @@ class CompressionSimulation:
|
|
|
353
353
|
bvh_filename: Optional[str] = None
|
|
354
354
|
# Sampling resolution (longest axis) assigned for the pass-2 sample job.
|
|
355
355
|
sample_resolution: Optional[int] = None
|
|
356
|
+
# From the prep metrics job; None until prep completes.
|
|
357
|
+
interior_volume: Optional[float] = None
|
|
356
358
|
patch: dict = field(default_factory=dict)
|
|
357
359
|
jobs: dict[str, Any] = field(default_factory=lambda: {})
|
|
358
360
|
part_unique_name = ""
|
|
@@ -374,6 +376,7 @@ class CompressionSimulation:
|
|
|
374
376
|
"is_piston": isinstance(self.part, ExperimentPistonBase),
|
|
375
377
|
"part_name": self.part.name,
|
|
376
378
|
"disabled": self.disabled,
|
|
379
|
+
"interior_volume": self.interior_volume,
|
|
377
380
|
}
|
|
378
381
|
|
|
379
382
|
def restore_from_state_dict(self, saved: dict):
|
|
@@ -382,6 +385,7 @@ class CompressionSimulation:
|
|
|
382
385
|
self.material_name = saved["material_name"]
|
|
383
386
|
self.jobs = saved["jobs"]
|
|
384
387
|
self.disabled = saved.get("disabled", False)
|
|
388
|
+
self.interior_volume = saved.get("interior_volume")
|
|
385
389
|
|
|
386
390
|
client: MetafoldClient
|
|
387
391
|
project_id: str = ""
|
|
@@ -425,7 +429,7 @@ class CompressionSimulation:
|
|
|
425
429
|
simulation_name: str,
|
|
426
430
|
project_id: str = "",
|
|
427
431
|
stl_folder_path: str = "PLY",
|
|
428
|
-
env_source: str = ".env.dev",
|
|
432
|
+
env_source: str | None = ".env.dev",
|
|
429
433
|
output_path: str = "",
|
|
430
434
|
client: Optional[MetafoldClient] = None,
|
|
431
435
|
simulation_parameters: SimulationParameters = SimulationParameters(),
|
|
@@ -642,7 +646,8 @@ class CompressionSimulation:
|
|
|
642
646
|
return created.id
|
|
643
647
|
|
|
644
648
|
def setup_client(self, env_source, project_id) -> MetafoldClient:
|
|
645
|
-
|
|
649
|
+
if env_source is not None:
|
|
650
|
+
load_dotenv(env_source)
|
|
646
651
|
|
|
647
652
|
if not project_id:
|
|
648
653
|
self.project_id = os.environ.get("METAFOLD_PROJECT_ID", "")
|
|
@@ -824,6 +829,24 @@ class CompressionSimulation:
|
|
|
824
829
|
params[f"{sample_mesh_job}.resolution"] = f"{resolution}"
|
|
825
830
|
part_info.jobs["sample-mesh"] = sample_mesh_job
|
|
826
831
|
|
|
832
|
+
# Metrics run in prep (not the main workflow) so interior volume
|
|
833
|
+
# is known before dispatch, when the server manifest is uploaded.
|
|
834
|
+
if self._is_analysis_target(part_info.part) and self._get_step(
|
|
835
|
+
WorkflowStepType.METRICS, part_info.part.name
|
|
836
|
+
):
|
|
837
|
+
metrics_job = f"metrics-{unique_name}"
|
|
838
|
+
jobs[metrics_job] = {
|
|
839
|
+
"type": "implicit/metrics",
|
|
840
|
+
"needs": [sample_mesh_job],
|
|
841
|
+
"assets": {"volume": sample_mesh_job},
|
|
842
|
+
"parameters": {
|
|
843
|
+
"volume_size": {"job": sample_mesh_job, "parameter": "patch_size"},
|
|
844
|
+
"volume_offset": {"job": sample_mesh_job, "parameter": "patch_offset"},
|
|
845
|
+
"volume_resolution": {"job": sample_mesh_job, "parameter": "patch_resolution"},
|
|
846
|
+
},
|
|
847
|
+
}
|
|
848
|
+
part_info.jobs["metrics"] = metrics_job
|
|
849
|
+
|
|
827
850
|
workflow_yaml = yaml.dump({"jobs": jobs}, default_flow_style=False)
|
|
828
851
|
return workflow_yaml, params, assets
|
|
829
852
|
|
|
@@ -1025,6 +1048,30 @@ class CompressionSimulation:
|
|
|
1025
1048
|
if volume_asset is not None:
|
|
1026
1049
|
info.volume_filename = volume_asset.filename
|
|
1027
1050
|
|
|
1051
|
+
metrics_job = prep_workflow_jobs.get(info.jobs.get("metrics", ""))
|
|
1052
|
+
if metrics_job is not None:
|
|
1053
|
+
raw = metrics_job.outputs.params.get("interior_volume")
|
|
1054
|
+
if raw is not None:
|
|
1055
|
+
info.interior_volume = float(raw)
|
|
1056
|
+
|
|
1057
|
+
def _total_interior_volume(self, workflow: Optional[Workflow] = None) -> float:
|
|
1058
|
+
"""Sum interior volumes across analysis-target parts. Falls back to
|
|
1059
|
+
the main-workflow metrics readback for experiments dispatched before
|
|
1060
|
+
metrics moved to the prep workflow."""
|
|
1061
|
+
total = 0.0
|
|
1062
|
+
for part_info in self.part_infos:
|
|
1063
|
+
if not self._is_analysis_target(part_info.part) or part_info.disabled:
|
|
1064
|
+
continue
|
|
1065
|
+
if part_info.interior_volume is not None:
|
|
1066
|
+
total += part_info.interior_volume
|
|
1067
|
+
continue
|
|
1068
|
+
metrics_job = part_info.jobs.get("metrics", "")
|
|
1069
|
+
if workflow is not None and metrics_job:
|
|
1070
|
+
raw = workflow.get_parameter(f"{metrics_job}.interior_volume")
|
|
1071
|
+
if raw is not None:
|
|
1072
|
+
total += float(raw)
|
|
1073
|
+
return total
|
|
1074
|
+
|
|
1028
1075
|
def get_part_info(self, name):
|
|
1029
1076
|
for info in self.part_infos:
|
|
1030
1077
|
if info.part.name == name:
|
|
@@ -1218,6 +1265,9 @@ class CompressionSimulation:
|
|
|
1218
1265
|
ElementTree.indent(self.ups)
|
|
1219
1266
|
|
|
1220
1267
|
def _add_to_workflow_metrics(self, part_infos: list[PartInfo]):
|
|
1268
|
+
# Duplicates the prep metrics jobs (which feed the server manifest):
|
|
1269
|
+
# removing these changed main-workflow scheduling and left it pending,
|
|
1270
|
+
# so they stay until that's understood.
|
|
1221
1271
|
for part_info in part_infos:
|
|
1222
1272
|
part = part_info.part
|
|
1223
1273
|
if not self._is_analysis_target(part):
|
|
@@ -1241,7 +1291,6 @@ class CompressionSimulation:
|
|
|
1241
1291
|
self.workflow_params[f"{metrics_job_name}.volume_resolution"] = json.dumps(
|
|
1242
1292
|
part_info.patch["resolution"]
|
|
1243
1293
|
)
|
|
1244
|
-
part_info.jobs["metrics"] = metrics_job_name
|
|
1245
1294
|
|
|
1246
1295
|
def _add_to_workflow_compress(self, part_infos: list[PartInfo]):
|
|
1247
1296
|
compress_job_name = "compress"
|
|
@@ -2046,16 +2095,7 @@ class CompressionSimulation:
|
|
|
2046
2095
|
data["ply"] = f"{name}/ply/part.####.ply"
|
|
2047
2096
|
|
|
2048
2097
|
# Sum interior volumes from all parts
|
|
2049
|
-
total_volume =
|
|
2050
|
-
for part_info in self.part_infos:
|
|
2051
|
-
if self._is_analysis_target(part_info.part):
|
|
2052
|
-
if part_info.disabled:
|
|
2053
|
-
continue
|
|
2054
|
-
metrics_job = part_info.jobs.get("metrics", "")
|
|
2055
|
-
if metrics_job:
|
|
2056
|
-
raw = w.get_parameter(f"{metrics_job}.interior_volume")
|
|
2057
|
-
if raw is not None:
|
|
2058
|
-
total_volume += float(raw)
|
|
2098
|
+
total_volume = self._total_interior_volume(w)
|
|
2059
2099
|
|
|
2060
2100
|
result.update(
|
|
2061
2101
|
{
|
|
@@ -2279,18 +2319,7 @@ class CompressionSimulation:
|
|
|
2279
2319
|
}
|
|
2280
2320
|
|
|
2281
2321
|
# Sum interior volumes across analysis-target parts.
|
|
2282
|
-
total_volume =
|
|
2283
|
-
for part_info in self.part_infos:
|
|
2284
|
-
if not self._is_analysis_target(part_info.part):
|
|
2285
|
-
continue
|
|
2286
|
-
if part_info.disabled:
|
|
2287
|
-
continue
|
|
2288
|
-
metrics_job = part_info.jobs.get("metrics", "")
|
|
2289
|
-
if not metrics_job:
|
|
2290
|
-
continue
|
|
2291
|
-
raw = w.get_parameter(f"{metrics_job}.interior_volume")
|
|
2292
|
-
if raw is not None:
|
|
2293
|
-
total_volume += float(raw)
|
|
2322
|
+
total_volume = self._total_interior_volume(w)
|
|
2294
2323
|
|
|
2295
2324
|
result.update(
|
|
2296
2325
|
{
|
|
@@ -2445,19 +2474,7 @@ class CompressionSimulation:
|
|
|
2445
2474
|
Volume is inlined (from prep workflow parameters); energy values are
|
|
2446
2475
|
refs that the server resolves from the energy-metrics job output."""
|
|
2447
2476
|
scalars: dict[str, Any] = {}
|
|
2448
|
-
|
|
2449
|
-
total_volume = 0.0
|
|
2450
|
-
for part_info in self.part_infos:
|
|
2451
|
-
if not self._is_analysis_target(part_info.part):
|
|
2452
|
-
continue
|
|
2453
|
-
if part_info.disabled:
|
|
2454
|
-
continue
|
|
2455
|
-
metrics_job = part_info.jobs.get("metrics", "")
|
|
2456
|
-
if metrics_job:
|
|
2457
|
-
raw = workflow.get_parameter(f"{metrics_job}.interior_volume")
|
|
2458
|
-
if raw is not None:
|
|
2459
|
-
total_volume += float(raw)
|
|
2460
|
-
scalars["volume"] = total_volume
|
|
2477
|
+
scalars["volume"] = self._total_interior_volume(workflow)
|
|
2461
2478
|
|
|
2462
2479
|
if self._contains_step(WorkflowStepType.ENERGY_METRICS):
|
|
2463
2480
|
em_job_id = job_id_lookup.get("energy-metrics", "")
|
|
@@ -2717,7 +2734,7 @@ class CompressionSimulation:
|
|
|
2717
2734
|
"heading": "Absorbed Energy (J)",
|
|
2718
2735
|
"filtering": False,
|
|
2719
2736
|
}
|
|
2720
|
-
)
|
|
2737
|
+
)
|
|
2721
2738
|
self.manifest["resultsListConfig"].append(
|
|
2722
2739
|
{
|
|
2723
2740
|
"key": "loadingEnergy",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metafold
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Metafold SDK for Python
|
|
5
5
|
Author-email: Metafold 3D <info@metafold3d.com>
|
|
6
6
|
License: Copyright 2024 Metafold 3D
|
|
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.13
|
|
26
26
|
Classifier: Topic :: Scientific/Engineering
|
|
27
27
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Requires-Dist: attrs~=23.2
|
|
@@ -38,7 +38,7 @@ Requires-Dist: dotenv>=0.9.9; extra == "simulation"
|
|
|
38
38
|
Requires-Dist: pandas>=2.3.3; extra == "simulation"
|
|
39
39
|
Requires-Dist: plyfile>=1.1.3; extra == "simulation"
|
|
40
40
|
Requires-Dist: pyyaml>=6.0.3; extra == "simulation"
|
|
41
|
-
Requires-Dist: simulation-configurator==0.
|
|
41
|
+
Requires-Dist: simulation-configurator==0.2.*; extra == "simulation"
|
|
42
42
|
Requires-Dist: tables>=3.11.1; extra == "simulation"
|
|
43
43
|
Dynamic: license-file
|
|
44
44
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "metafold"
|
|
7
|
-
version = "0.12.
|
|
7
|
+
version = "0.12.1"
|
|
8
8
|
authors = [
|
|
9
9
|
{name = "Metafold 3D", email = "info@metafold3d.com"},
|
|
10
10
|
]
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
readme = "README.md"
|
|
19
19
|
license = {file = "LICENSE"}
|
|
20
20
|
description = "Metafold SDK for Python"
|
|
21
|
-
requires-python = ">=3.
|
|
21
|
+
requires-python = ">=3.11"
|
|
22
22
|
keywords = ["metafold", "api", "sdk", "implicit geometry"]
|
|
23
23
|
classifiers = [
|
|
24
24
|
"Development Status :: 3 - Alpha",
|
|
@@ -38,7 +38,7 @@ simulation = [
|
|
|
38
38
|
"pandas>=2.3.3",
|
|
39
39
|
"plyfile>=1.1.3",
|
|
40
40
|
"pyyaml>=6.0.3",
|
|
41
|
-
"simulation-configurator==0.
|
|
41
|
+
"simulation-configurator==0.2.*",
|
|
42
42
|
"tables>=3.11.1",
|
|
43
43
|
]
|
|
44
44
|
|
|
@@ -68,7 +68,7 @@ packages = ["metafold", "metafold.simulation"]
|
|
|
68
68
|
environments = ["sys_platform == 'linux'"]
|
|
69
69
|
|
|
70
70
|
[tool.uv.sources]
|
|
71
|
-
simulation-configurator = { index = "
|
|
71
|
+
simulation-configurator = { index = "gitlab" }
|
|
72
72
|
|
|
73
73
|
[[tool.mypy.overrides]]
|
|
74
74
|
module = [
|
|
@@ -79,7 +79,7 @@ module = [
|
|
|
79
79
|
ignore_missing_imports = true
|
|
80
80
|
|
|
81
81
|
[[tool.uv.index]]
|
|
82
|
-
name = "
|
|
82
|
+
name = "gitlab"
|
|
83
83
|
url = "https://gitlab.com/api/v4/projects/75460563/packages/pypi/simple"
|
|
84
84
|
explicit = true
|
|
85
85
|
authenticate = "always"
|
|
@@ -167,13 +167,15 @@ class TestBuildWorkflow:
|
|
|
167
167
|
assert isinstance(piston.part, ExperimentPistonBase)
|
|
168
168
|
assert piston.material_index == 0
|
|
169
169
|
|
|
170
|
-
def
|
|
171
|
-
|
|
170
|
+
def test_prep_workflow_has_per_part_metrics_jobs(self, prepared_sim):
|
|
171
|
+
mesh_parts = [p for p in prepared_sim.part_infos if hasattr(p.part, "filename")]
|
|
172
|
+
yaml_str, _, _ = prepared_sim._build_sample_workflow_for_batch(mesh_parts)
|
|
172
173
|
loaded = yaml.safe_load(yaml_str)
|
|
173
|
-
# one metrics job per non-piston mesh part
|
|
174
|
+
# one metrics job per non-piston mesh part, chained to its sample job
|
|
174
175
|
for info in prepared_sim.part_infos:
|
|
175
176
|
if not isinstance(info.part, ExperimentPistonBase) and hasattr(info.part, "filename"):
|
|
176
|
-
|
|
177
|
+
job = loaded["jobs"][f"metrics-{info.part_unique_name}"]
|
|
178
|
+
assert job["needs"] == [f"sample-mesh-{info.part_unique_name}"]
|
|
177
179
|
|
|
178
180
|
def test_build_workflow_compress_has_velocity(self, prepared_sim):
|
|
179
181
|
_, params = prepared_sim.build_workflow()
|
|
@@ -1130,6 +1132,7 @@ class TestSetupClient:
|
|
|
1130
1132
|
simulation_name="t",
|
|
1131
1133
|
stl_folder_path=str(ply_folder),
|
|
1132
1134
|
output_path=str(tmp_path / "out"),
|
|
1135
|
+
env_source=None, # Ensure local .env files do not pollute test environment
|
|
1133
1136
|
**kwargs,
|
|
1134
1137
|
)
|
|
1135
1138
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|