cad-to-dagmc 0.5.0__py3-none-any.whl → 0.5.1__py3-none-any.whl

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.

Potentially problematic release.


This version of cad-to-dagmc might be problematic. Click here for more details.

_version.py CHANGED
@@ -2,7 +2,15 @@
2
2
  # don't change, don't track in version control
3
3
  TYPE_CHECKING = False
4
4
  if TYPE_CHECKING:
5
- from typing import Tuple
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
6
9
 
7
- __version__ = version = '0.5.0' # type: str
8
- __version_tuple__ = version_tuple = (0, 5, 0) # type: Tuple[int | str, ...]
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.5.1'
16
+ __version_tuple__ = version_tuple = (0, 5, 1)
cad_to_dagmc/__init__.py CHANGED
@@ -13,9 +13,4 @@ except PackageNotFoundError:
13
13
 
14
14
  __all__ = ["__version__"]
15
15
 
16
- from .core import CadToDagmc
17
- from .vertices_to_h5m import vertices_to_h5m
18
- from .brep_part_finder import *
19
- from .brep_to_h5m import *
20
-
21
- [CadToDagmc, vertices_to_h5m]
16
+ from .core import *
cad_to_dagmc/core.py CHANGED
@@ -1,23 +1,375 @@
1
1
  import typing
2
+ from pathlib import Path
3
+
4
+ import cadquery as cq
5
+ import gmsh
6
+ import numpy as np
7
+ import OCP
8
+ import trimesh
2
9
  from cadquery import importers
10
+ from pymoab import core, types
11
+
12
+
13
+ def fix_normals(vertices: typing.Sequence, triangles_in_each_volume: typing.Sequence):
14
+ fixed_triangles = []
15
+ for triangles in triangles_in_each_volume:
16
+ fixed_triangles.append(fix_normal(vertices, triangles))
17
+ return fixed_triangles
18
+
19
+
20
+ def fix_normal(vertices: typing.Sequence, triangles: typing.Sequence):
21
+ mesh = trimesh.Trimesh(vertices=vertices, faces=triangles, process=False)
22
+
23
+ mesh.fix_normals()
24
+
25
+ return mesh.faces
26
+
27
+
28
+ def define_moab_core_and_tags() -> typing.Tuple[core.Core, dict]:
29
+ """Creates a MOAB Core instance which can be built up by adding sets of
30
+ triangles to the instance
31
+
32
+ Returns:
33
+ (pymoab Core): A pymoab.core.Core() instance
34
+ (pymoab tag_handle): A pymoab.core.tag_get_handle() instance
35
+ """
36
+
37
+ # create pymoab instance
38
+ moab_core = core.Core()
39
+
40
+ tags = dict()
41
+
42
+ sense_tag_name = "GEOM_SENSE_2"
43
+ sense_tag_size = 2
44
+ tags["surf_sense"] = moab_core.tag_get_handle(
45
+ sense_tag_name,
46
+ sense_tag_size,
47
+ types.MB_TYPE_HANDLE,
48
+ types.MB_TAG_SPARSE,
49
+ create_if_missing=True,
50
+ )
51
+
52
+ tags["category"] = moab_core.tag_get_handle(
53
+ types.CATEGORY_TAG_NAME,
54
+ types.CATEGORY_TAG_SIZE,
55
+ types.MB_TYPE_OPAQUE,
56
+ types.MB_TAG_SPARSE,
57
+ create_if_missing=True,
58
+ )
59
+
60
+ tags["name"] = moab_core.tag_get_handle(
61
+ types.NAME_TAG_NAME,
62
+ types.NAME_TAG_SIZE,
63
+ types.MB_TYPE_OPAQUE,
64
+ types.MB_TAG_SPARSE,
65
+ create_if_missing=True,
66
+ )
67
+
68
+ tags["geom_dimension"] = moab_core.tag_get_handle(
69
+ types.GEOM_DIMENSION_TAG_NAME,
70
+ 1,
71
+ types.MB_TYPE_INTEGER,
72
+ types.MB_TAG_DENSE,
73
+ create_if_missing=True,
74
+ )
75
+
76
+ # Global ID is a default tag, just need the name to retrieve
77
+ tags["global_id"] = moab_core.tag_get_handle(types.GLOBAL_ID_TAG_NAME)
78
+
79
+ return moab_core, tags
80
+
81
+
82
+ def vertices_to_h5m(
83
+ vertices: typing.Union[
84
+ typing.Iterable[typing.Tuple[float, float, float]],
85
+ typing.Iterable["cadquery.occ_impl.geom.Vector"],
86
+ ],
87
+ triangles_by_solid_by_face: typing.Iterable[typing.Iterable[typing.Tuple[int, int, int]]],
88
+ material_tags: typing.Iterable[str],
89
+ h5m_filename="dagmc.h5m",
90
+ ):
91
+ """Converts vertices and triangle sets into a tagged h5m file compatible
92
+ with DAGMC enabled neutronics simulations
93
+
94
+ Args:
95
+ vertices:
96
+ triangles:
97
+ material_tags:
98
+ h5m_filename:
99
+ """
100
+
101
+ if len(material_tags) != len(triangles_by_solid_by_face):
102
+ msg = f"The number of material_tags provided is {len(material_tags)} and the number of sets of triangles is {len(triangles_by_solid_by_face)}. You must provide one material_tag for every triangle set"
103
+ raise ValueError(msg)
104
+
105
+ # limited attribute checking to see if user passed in a list of CadQuery vectors
106
+ if hasattr(vertices[0], "x") and hasattr(vertices[0], "y") and hasattr(vertices[0], "z"):
107
+ vertices_floats = []
108
+ for vert in vertices:
109
+ vertices_floats.append((vert.x, vert.y, vert.z))
110
+ else:
111
+ vertices_floats = vertices
112
+
113
+ face_ids_with_solid_ids = {}
114
+ for solid_id, triangles_on_each_face in triangles_by_solid_by_face.items():
115
+ for face_id, triangles_on_face in triangles_on_each_face.items():
116
+ if face_id in face_ids_with_solid_ids.keys():
117
+ face_ids_with_solid_ids[face_id].append(solid_id)
118
+ else:
119
+ face_ids_with_solid_ids[face_id] = [solid_id]
120
+
121
+ moab_core, tags = define_moab_core_and_tags()
122
+
123
+ volume_sets_by_solid_id = {}
124
+ for material_tag, (solid_id, triangles_on_each_face) in zip(
125
+ material_tags, triangles_by_solid_by_face.items()
126
+ ):
127
+ volume_set = moab_core.create_meshset()
128
+ volume_sets_by_solid_id[solid_id] = volume_set
3
129
 
4
- # from cadquery import Assembly
5
- # from OCP.GCPnts import GCPnts_QuasiUniformDeflection
130
+ added_surfaces_ids = {}
131
+ for material_tag, (solid_id, triangles_on_each_face) in zip(
132
+ material_tags, triangles_by_solid_by_face.items()
133
+ ):
134
+ volume_set = volume_sets_by_solid_id[solid_id]
6
135
 
7
- # from cadquery.occ_impl import shapes
8
- import OCP
9
- import cadquery as cq
136
+ moab_core.tag_set_data(tags["global_id"], volume_set, solid_id)
137
+ moab_core.tag_set_data(tags["geom_dimension"], volume_set, 3)
138
+ moab_core.tag_set_data(tags["category"], volume_set, "Volume")
139
+
140
+ group_set = moab_core.create_meshset()
141
+ moab_core.tag_set_data(tags["category"], group_set, "Group")
142
+ moab_core.tag_set_data(tags["name"], group_set, f"mat:{material_tag}")
143
+ moab_core.tag_set_data(tags["global_id"], group_set, solid_id)
144
+ # moab_core.tag_set_data(tags["geom_dimension"], group_set, 4)
145
+
146
+ for face_id, triangles_on_face in triangles_on_each_face.items():
147
+ if face_id not in added_surfaces_ids.keys():
148
+ face_set = moab_core.create_meshset()
149
+ moab_core.tag_set_data(tags["global_id"], face_set, face_id)
150
+ moab_core.tag_set_data(tags["geom_dimension"], face_set, 2)
151
+ moab_core.tag_set_data(tags["category"], face_set, "Surface")
152
+
153
+ if len(face_ids_with_solid_ids[face_id]) == 2:
154
+ other_solid_id = face_ids_with_solid_ids[face_id][1]
155
+ other_volume_set = volume_sets_by_solid_id[other_solid_id]
156
+ sense_data = np.array([other_volume_set, volume_set], dtype="uint64")
157
+ else:
158
+ sense_data = np.array([volume_set, 0], dtype="uint64")
159
+
160
+ moab_core.tag_set_data(tags["surf_sense"], face_set, sense_data)
161
+
162
+ moab_verts = moab_core.create_vertices(vertices)
163
+ moab_core.add_entity(face_set, moab_verts)
164
+
165
+ for triangle in triangles_on_face:
166
+ tri = (
167
+ moab_verts[int(triangle[0])],
168
+ moab_verts[int(triangle[1])],
169
+ moab_verts[int(triangle[2])],
170
+ )
171
+
172
+ moab_triangle = moab_core.create_element(types.MBTRI, tri)
173
+ moab_core.add_entity(face_set, moab_triangle)
174
+
175
+ added_surfaces_ids[face_id] = face_set
176
+ else:
177
+ face_set = added_surfaces_ids[face_id]
178
+
179
+ other_solid_id = face_ids_with_solid_ids[face_id][0]
180
+
181
+ other_volume_set = volume_sets_by_solid_id[other_solid_id]
182
+
183
+ sense_data = np.array([other_volume_set, volume_set], dtype="uint64")
184
+ moab_core.tag_set_data(tags["surf_sense"], face_set, sense_data)
185
+
186
+ moab_core.add_parent_child(volume_set, face_set)
187
+
188
+ moab_core.add_entity(group_set, volume_set)
189
+
190
+ all_sets = moab_core.get_entities_by_handle(0)
191
+
192
+ file_set = moab_core.create_meshset()
193
+
194
+ moab_core.add_entities(file_set, all_sets)
195
+
196
+ moab_core.write_file(h5m_filename)
197
+
198
+ return h5m_filename
199
+
200
+
201
+ def mesh_brep(
202
+ brep_object: str,
203
+ min_mesh_size: float = 1,
204
+ max_mesh_size: float = 10,
205
+ mesh_algorithm: int = 1,
206
+ ):
207
+ """Creates a conformal surface meshes of the volumes in a Brep file using
208
+ Gmsh.
209
+
210
+ Args:
211
+ brep_object: the filename of the Brep file to convert
212
+ min_mesh_size: the minimum mesh element size to use in Gmsh. Passed
213
+ into gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)
214
+ max_mesh_size: the maximum mesh element size to use in Gmsh. Passed
215
+ into gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)
216
+ mesh_algorithm: The Gmsh mesh algorithm number to use. Passed into
217
+ gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
218
+
219
+ Returns:
220
+ The resulting gmsh object and volumes
221
+ """
222
+
223
+ gmsh.initialize()
224
+ gmsh.option.setNumber("General.Terminal", 1)
225
+ gmsh.model.add("made_with_brep_to_h5m_package")
226
+ volumes = gmsh.model.occ.importShapesNativePointer(brep_object)
227
+ gmsh.model.occ.synchronize()
228
+
229
+ gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
230
+ gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)
231
+ gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)
232
+ gmsh.model.mesh.generate(2)
233
+
234
+ return gmsh, volumes
235
+
236
+
237
+ def mesh_to_h5m_in_memory_method(
238
+ volumes,
239
+ material_tags: typing.Iterable[str],
240
+ h5m_filename: str = "dagmc.h5m",
241
+ msh_filename=None,
242
+ ) -> str:
243
+ """Converts gmsh volumes into a DAGMC h5m file.
244
+
245
+ Args:
246
+ volumes: the volumes in the gmsh file, found with gmsh.model.occ.importShapes
247
+ material_tags: A list of material tags to tag the DAGMC volumes with.
248
+ Should be in the same order as the volumes
249
+ h5m_filename: the filename of the DAGMC h5m file to write
250
+
251
+ Returns:
252
+ The filename of the h5m file produced
253
+ """
254
+
255
+ if isinstance(material_tags, str):
256
+ msg = f"material_tags should be a list of strings, not a single string."
257
+ raise ValueError(msg)
258
+
259
+ if len(volumes) != len(material_tags):
260
+ msg = f"{len(volumes)} volumes found in Brep file is not equal to the number of material_tags {len(material_tags)} provided."
261
+ raise ValueError(msg)
262
+
263
+ n = 3 # number of verts in a triangles
264
+ triangles_by_solid_by_face = {}
265
+ for dim_and_vol in volumes:
266
+ # removes all groups so that the following getEntitiesForPhysicalGroup
267
+ # command only finds surfaces for the volume
268
+ gmsh.model.removePhysicalGroups()
269
+
270
+ vol_id = dim_and_vol[1]
271
+ entities_in_volume = gmsh.model.getAdjacencies(3, vol_id)
272
+ surfaces_in_volume = entities_in_volume[1]
273
+ ps = gmsh.model.addPhysicalGroup(2, surfaces_in_volume)
274
+ gmsh.model.setPhysicalName(2, ps, f"surfaces_on_volume_{vol_id}")
275
+
276
+ groups = gmsh.model.getPhysicalGroups()
277
+ group = groups[0]
278
+ # for group in groups:
279
+ dim = group[0]
280
+ tag = group[1]
281
+
282
+ surfaces = gmsh.model.getEntitiesForPhysicalGroup(dim, tag)
283
+
284
+ # nodes_in_all_surfaces = []
285
+ nodes_in_each_surface = {}
286
+ for surface in surfaces:
287
+ _, _, nodeTags = gmsh.model.mesh.getElements(2, surface)
288
+ nodeTags = nodeTags[0].tolist()
289
+ shifted_node_tags = []
290
+ for nodeTag in nodeTags:
291
+ shifted_node_tags.append(nodeTag - 1)
292
+ grouped_node_tags = [
293
+ shifted_node_tags[i : i + n] for i in range(0, len(shifted_node_tags), n)
294
+ ]
295
+ nodes_in_each_surface[surface] = grouped_node_tags
296
+ triangles_by_solid_by_face[vol_id] = nodes_in_each_surface
297
+
298
+ _, all_coords, _ = gmsh.model.mesh.getNodes()
299
+
300
+ vertices = [all_coords[i : i + n].tolist() for i in range(0, len(all_coords), n)]
301
+
302
+ if msh_filename is not None:
303
+ gmsh.write(msh_filename)
304
+
305
+ gmsh.finalize()
306
+
307
+ # checks and fixes triangle fix_normals within vertices_to_h5m
308
+ return vertices_to_h5m(
309
+ vertices=vertices,
310
+ triangles_by_solid_by_face=triangles_by_solid_by_face,
311
+ material_tags=material_tags,
312
+ h5m_filename=h5m_filename,
313
+ )
314
+
315
+
316
+ def get_ids_from_assembly(assembly):
317
+ ids = []
318
+ for obj, name, loc, _ in assembly:
319
+ ids.append(name)
320
+ return ids
321
+
322
+
323
+ def get_ids_from_imprinted_assembly(solid_id_dict):
324
+ ids = []
325
+ for id in list(solid_id_dict.values()):
326
+ ids.append(id[0])
327
+ return ids
328
+
329
+
330
+ def order_material_ids_by_brep_order(original_ids, scrambled_id, material_tags):
331
+ material_tags_in_brep_order = []
332
+ for brep_id in scrambled_id:
333
+ id_of_solid_in_org = original_ids.index(brep_id)
334
+ material_tags_in_brep_order.append(material_tags[id_of_solid_in_org])
335
+ return material_tags_in_brep_order
336
+
337
+
338
+ def merge_surfaces(parts):
339
+ """Merges surfaces in the geometry that are the same. More details on
340
+ the merging process in the DAGMC docs
341
+ https://svalinn.github.io/DAGMC/usersguide/cubit_basics.html"""
342
+
343
+ # solids = geometry.Solids()
344
+
345
+ bldr = OCP.BOPAlgo.BOPAlgo_Splitter()
346
+
347
+ if len(parts) == 1:
348
+ # merged_solid = cq.Compound(solids)
349
+
350
+ if isinstance(parts[0], (cq.occ_impl.shapes.Compound, cq.occ_impl.shapes.Solid)):
351
+ # stp file
352
+ return parts[0], parts[0].wrapped
353
+ else:
354
+ return parts[0], parts[0].toOCC()
355
+
356
+ # else:
357
+ for solid in parts:
358
+ # checks if solid is a compound as .val() is not needed for compounds
359
+ if isinstance(solid, (cq.occ_impl.shapes.Compound, cq.occ_impl.shapes.Solid)):
360
+ bldr.AddArgument(solid.wrapped)
361
+ else:
362
+ bldr.AddArgument(solid.val().wrapped)
363
+
364
+ bldr.SetNonDestructive(True)
365
+
366
+ bldr.Perform()
367
+
368
+ bldr.Images()
10
369
 
11
- # from OCP.TopLoc import TopLoc_Location
12
- # from OCP.BRep import BRep_Tool
13
- # from OCP.TopAbs import TopAbs_Orientation
370
+ merged_solid = cq.Compound(bldr.Shape())
14
371
 
15
- from .brep_to_h5m import mesh_brep, mesh_to_h5m_in_memory_method
16
- from .brep_part_finder import (
17
- get_ids_from_assembly,
18
- get_ids_from_imprinted_assembly,
19
- order_material_ids_by_brep_order,
20
- )
372
+ return merged_solid, merged_solid.wrapped
21
373
 
22
374
 
23
375
  class CadToDagmc:
@@ -99,16 +451,18 @@ class CadToDagmc:
99
451
  for part in self.parts:
100
452
  assembly.add(part)
101
453
 
102
- (
103
- imprinted_assembly,
104
- imprinted_solids_with_original_id,
105
- ) = cq.occ_impl.assembly.imprint(assembly)
454
+ imprinted_assembly, imprinted_solids_with_org_id = cq.occ_impl.assembly.imprint(assembly)
106
455
 
107
- original_ids = get_ids_from_assembly(assembly)
108
- scrambled_ids = get_ids_from_imprinted_assembly(
109
- imprinted_solids_with_original_id
456
+ gmsh, volumes = mesh_brep(
457
+ brep_object=imprinted_assembly.wrapped._address(),
458
+ min_mesh_size=min_mesh_size,
459
+ max_mesh_size=max_mesh_size,
460
+ mesh_algorithm=mesh_algorithm,
110
461
  )
111
462
 
463
+ original_ids = get_ids_from_assembly(assembly)
464
+ scrambled_ids = get_ids_from_imprinted_assembly(imprinted_solids_with_org_id)
465
+
112
466
  # both id lists should be the same length as each other and the same
113
467
  # length as the self.material_tags
114
468
 
@@ -116,13 +470,6 @@ class CadToDagmc:
116
470
  original_ids, scrambled_ids, self.material_tags
117
471
  )
118
472
 
119
- gmsh, volumes = mesh_brep(
120
- brep_object=imprinted_assembly.wrapped._address(),
121
- min_mesh_size=min_mesh_size,
122
- max_mesh_size=max_mesh_size,
123
- mesh_algorithm=mesh_algorithm,
124
- )
125
-
126
473
  h5m_filename = mesh_to_h5m_in_memory_method(
127
474
  volumes=volumes,
128
475
  material_tags=material_tags_in_brep_order,
@@ -130,42 +477,3 @@ class CadToDagmc:
130
477
  msh_filename=msh_filename,
131
478
  )
132
479
  return h5m_filename
133
-
134
-
135
- def merge_surfaces(parts):
136
- """Merges surfaces in the geometry that are the same. More details on
137
- the merging process in the DAGMC docs
138
- https://svalinn.github.io/DAGMC/usersguide/cubit_basics.html"""
139
-
140
- # solids = geometry.Solids()
141
-
142
- bldr = OCP.BOPAlgo.BOPAlgo_Splitter()
143
-
144
- if len(parts) == 1:
145
- # merged_solid = cq.Compound(solids)
146
-
147
- if isinstance(
148
- parts[0], (cq.occ_impl.shapes.Compound, cq.occ_impl.shapes.Solid)
149
- ):
150
- # stp file
151
- return parts[0], parts[0].wrapped
152
- else:
153
- return parts[0], parts[0].toOCC()
154
-
155
- # else:
156
- for solid in parts:
157
- # checks if solid is a compound as .val() is not needed for compounds
158
- if isinstance(solid, (cq.occ_impl.shapes.Compound, cq.occ_impl.shapes.Solid)):
159
- bldr.AddArgument(solid.wrapped)
160
- else:
161
- bldr.AddArgument(solid.val().wrapped)
162
-
163
- bldr.SetNonDestructive(True)
164
-
165
- bldr.Perform()
166
-
167
- bldr.Images()
168
-
169
- merged_solid = cq.Compound(bldr.Shape())
170
-
171
- return merged_solid, merged_solid.wrapped
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cad-to-dagmc
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: Converts CAD files to a DAGMC h5m file
5
5
  Author-email: Jonathan Shimwell <mail@jshimwell.com>
6
6
  Project-URL: Homepage, https://github.com/fusion-energy/cad_to_dagmc
@@ -20,24 +20,25 @@ Requires-Dist: pytest ; extra == 'tests'
20
20
 
21
21
  [![N|Python](https://www.python.org/static/community_logos/python-powered-w-100x40.png)](https://www.python.org)
22
22
 
23
- [![CI with install](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_install.yml/badge.svg)](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_install.yml)
23
+ [![CI with model benchmark zoo](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_benchmarks.yml/badge.svg)](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_benchmarks.yml) Testing package and running examples
24
+
25
+ [![CI with model benchmark zoo](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_benchmarks.yml/badge.svg?branch=main)](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/ci_with_benchmarks.yml) Testing with [Model Benchmark Zoo](https://github.com/fusion-energy/model_benchmark_zoo)
24
26
 
25
27
  [![Upload Python Package](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/python-publish.yml/badge.svg)](https://github.com/fusion-energy/cad_to_dagmc/actions/workflows/python-publish.yml)
26
28
 
27
29
  [![PyPI](https://img.shields.io/pypi/v/cad_to_dagmc?color=brightgreen&label=pypi&logo=grebrightgreenen&logoColor=green)](https://pypi.org/project/cad_to_dagmc/)
28
30
 
29
- ___
30
31
 
31
32
  A minimal package that uses CadQuery functionality to convert CAD geometry to [DAGMC](https://github.com/svalinn/DAGMC/) h5m files
32
33
 
33
- This particular method of producing DAGMC compatible h5m files from CAD geometry
34
- is intended to convert STP files or [CadQuery](https://cadquery.readthedocs.io) objects to h5m file.
35
-
36
- One unique feature of this package is the ability to combine STP files with CadQuery objects.
37
- This allows for the addition of parametric geometry to static geometry.
34
+ This particular method of producing DAGMC compatible h5m files from CAD geometry is intended to convert STP files or [CadQuery](https://cadquery.readthedocs.io) objects to a DAGMC compatible h5m file.
38
35
 
39
36
  The resulting DAGMC geometry can then be used for simulations in [OpenMC](https://github.com/openmc-dev/openmc/) or [other supported codes](https://svalinn.github.io/DAGMC/).
40
37
 
38
+ This package is tested with [pytest tests](https://github.com/fusion-energy/cad_to_dagmc/tree/main/tests) and also the DAGMC geometry made with this package is compared to simulation carried out with native constructive solid geometry, see [Model Benchmark Zoo](https://github.com/fusion-energy/model_benchmark_zoo) for more details.
39
+
40
+ Also checkout these other packages that also create DAGMC geometry [CAD-to-OpenMC](https://github.com/openmsr/CAD_to_OpenMC), [Stellarmesh](https://github.com/Thea-Energy/stellarmesh)
41
+
41
42
  # Installation prerequisite
42
43
 
43
44
  In principle, any Conda/Mamba distribution will work. A few Conda/Mamba options are:
@@ -0,0 +1,8 @@
1
+ _version.py,sha256=8W5N0WKS0YIWUpfry5TouisDYjsMUIZ7Vc0crGGGQQU,411
2
+ cad_to_dagmc/__init__.py,sha256=fskHUTyCunSpnpJUvBfAYjx4uwDKXHTTiMP6GqnFRf0,494
3
+ cad_to_dagmc/core.py,sha256=rLJT6RhmaqQ2NQm9Nyxj1pqSz5TC0pgET0CvpQFI3TM,16643
4
+ cad_to_dagmc-0.5.1.dist-info/LICENSE,sha256=B8kznH_777JVNZ3HOKDc4Tj24F7wJ68ledaNYeL9sCw,1070
5
+ cad_to_dagmc-0.5.1.dist-info/METADATA,sha256=4RxJjQNV7RKMmB_xprO8LJ_OyuLSMlklUtmqaQvtyvo,5583
6
+ cad_to_dagmc-0.5.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
7
+ cad_to_dagmc-0.5.1.dist-info/top_level.txt,sha256=zTi8C64SEBsE5WOtPovnxhOzt-E6Oc5nC3RW6M_5aEA,22
8
+ cad_to_dagmc-0.5.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,36 +0,0 @@
1
- import cadquery as cq
2
-
3
-
4
- def convert_shape_to_iterable_of_shapes(shapes):
5
- if isinstance(shapes, cq.occ_impl.shapes.Compound):
6
- # brep route
7
- iterable_solids = shapes.Solids()
8
- elif isinstance(shapes, cq.Workplane):
9
- # workplane route
10
- iterable_solids = shapes.val().Solids()
11
- else:
12
- iterable_solids = shapes.Solids()
13
-
14
- return iterable_solids
15
-
16
-
17
- def get_ids_from_assembly(assembly):
18
- ids = []
19
- for obj, name, loc, _ in assembly:
20
- ids.append(name)
21
- return ids
22
-
23
-
24
- def get_ids_from_imprinted_assembly(solid_id_dict):
25
- ids = []
26
- for id in list(solid_id_dict.values()):
27
- ids.append(id[0])
28
- return ids
29
-
30
-
31
- def order_material_ids_by_brep_order(original_ids, scrambled_id, material_tags):
32
- material_tags_in_brep_order = []
33
- for brep_id in scrambled_id:
34
- id_of_solid_in_org = original_ids.index(brep_id)
35
- material_tags_in_brep_order.append(material_tags[id_of_solid_in_org])
36
- return material_tags_in_brep_order
@@ -1,121 +0,0 @@
1
- import gmsh
2
- from pathlib import Path
3
- from .vertices_to_h5m import vertices_to_h5m
4
- import typing
5
-
6
-
7
- def mesh_brep(
8
- brep_object: str,
9
- min_mesh_size: float = 1,
10
- max_mesh_size: float = 10,
11
- mesh_algorithm: int = 1,
12
- ):
13
- """Creates a conformal surface meshes of the volumes in a Brep file using
14
- Gmsh.
15
-
16
- Args:
17
- brep_object: the filename of the Brep file to convert
18
- min_mesh_size: the minimum mesh element size to use in Gmsh. Passed
19
- into gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)
20
- max_mesh_size: the maximum mesh element size to use in Gmsh. Passed
21
- into gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)
22
- mesh_algorithm: The Gmsh mesh algorithm number to use. Passed into
23
- gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
24
-
25
- Returns:
26
- The gmsh object and volumes in Brep file
27
- """
28
-
29
- gmsh.initialize()
30
- gmsh.option.setNumber("General.Terminal", 1)
31
- gmsh.model.add("made_with_brep_to_h5m_package")
32
- volumes = gmsh.model.occ.importShapesNativePointer(brep_object)
33
- # gmsh.model.occ.importShapes(brep_object)
34
- gmsh.model.occ.synchronize()
35
-
36
- gmsh.option.setNumber("Mesh.Algorithm", mesh_algorithm)
37
- gmsh.option.setNumber("Mesh.MeshSizeMin", min_mesh_size)
38
- gmsh.option.setNumber("Mesh.MeshSizeMax", max_mesh_size)
39
- gmsh.model.mesh.generate(2)
40
-
41
- return gmsh, volumes
42
-
43
-
44
- def mesh_to_h5m_in_memory_method(
45
- volumes,
46
- material_tags: typing.Iterable[str],
47
- h5m_filename: str = "dagmc.h5m",
48
- msh_filename=None,
49
- ) -> str:
50
- """Converts gmsh volumes into a DAGMC h5m file.
51
-
52
- Args:
53
- volumes: the volumes in the gmsh file, found with gmsh.model.occ.importShapes
54
- material_tags: A list of material tags to tag the DAGMC volumes with.
55
- Should be in the same order as the volumes
56
- h5m_filename: the filename of the DAGMC h5m file to write
57
-
58
- Returns:
59
- The filename of the h5m file produced
60
- """
61
-
62
- if isinstance(material_tags, str):
63
- msg = f"material_tags should be a list of strings, not a single string."
64
- raise ValueError(msg)
65
-
66
- if len(volumes) != len(material_tags):
67
- msg = f"{len(volumes)} volumes found in Brep file is not equal to the number of material_tags {len(material_tags)} provided."
68
- raise ValueError(msg)
69
-
70
- n = 3 # number of verts in a triangles
71
- triangles_by_solid_by_face = {}
72
- for dim_and_vol in volumes:
73
- # removes all groups so that the following getEntitiesForPhysicalGroup
74
- # command only finds surfaces for the volume
75
- gmsh.model.removePhysicalGroups()
76
-
77
- vol_id = dim_and_vol[1]
78
- entities_in_volume = gmsh.model.getAdjacencies(3, vol_id)
79
- surfaces_in_volume = entities_in_volume[1]
80
- ps = gmsh.model.addPhysicalGroup(2, surfaces_in_volume)
81
- gmsh.model.setPhysicalName(2, ps, f"surfaces_on_volume_{vol_id}")
82
-
83
- groups = gmsh.model.getPhysicalGroups()
84
- group = groups[0]
85
- # for group in groups:
86
- dim = group[0]
87
- tag = group[1]
88
-
89
- surfaces = gmsh.model.getEntitiesForPhysicalGroup(dim, tag)
90
-
91
- # nodes_in_all_surfaces = []
92
- nodes_in_each_surface = {}
93
- for surface in surfaces:
94
- _, _, nodeTags = gmsh.model.mesh.getElements(2, surface)
95
- nodeTags = nodeTags[0].tolist()
96
- shifted_node_tags = []
97
- for nodeTag in nodeTags:
98
- shifted_node_tags.append(nodeTag - 1)
99
- grouped_node_tags = [
100
- shifted_node_tags[i : i + n]
101
- for i in range(0, len(shifted_node_tags), n)
102
- ]
103
- nodes_in_each_surface[surface] = grouped_node_tags
104
- triangles_by_solid_by_face[vol_id] = nodes_in_each_surface
105
-
106
- _, all_coords, _ = gmsh.model.mesh.getNodes()
107
-
108
- vertices = [all_coords[i : i + n].tolist() for i in range(0, len(all_coords), n)]
109
-
110
- if msh_filename is not None:
111
- gmsh.write(msh_filename)
112
-
113
- gmsh.finalize()
114
-
115
- # checks and fixes triangle fix_normals within vertices_to_h5m
116
- return vertices_to_h5m(
117
- vertices=vertices,
118
- triangles_by_solid_by_face=triangles_by_solid_by_face,
119
- material_tags=material_tags,
120
- h5m_filename=h5m_filename,
121
- )
@@ -1,262 +0,0 @@
1
- from typing import Iterable, Tuple, Union
2
- import typing
3
- import numpy as np
4
- import trimesh
5
- from pymoab import core, types
6
-
7
-
8
- def fix_normals(vertices: list, triangles_in_each_volume: list):
9
- fixed_triangles = []
10
- for triangles in triangles_in_each_volume:
11
- fixed_triangles.append(fix_normal(vertices, triangles))
12
- return fixed_triangles
13
-
14
-
15
- def fix_normal(vertices: list, triangles: list):
16
- mesh = trimesh.Trimesh(vertices=vertices, faces=triangles, process=False)
17
-
18
- mesh.fix_normals()
19
-
20
- return mesh.faces
21
-
22
-
23
- def _define_moab_core_and_tags() -> Tuple[core.Core, dict]:
24
- """Creates a MOAB Core instance which can be built up by adding sets of
25
- triangles to the instance
26
-
27
- Returns:
28
- (pymoab Core): A pymoab.core.Core() instance
29
- (pymoab tag_handle): A pymoab.core.tag_get_handle() instance
30
- """
31
-
32
- # create pymoab instance
33
- moab_core = core.Core()
34
-
35
- tags = dict()
36
-
37
- sense_tag_name = "GEOM_SENSE_2"
38
- sense_tag_size = 2
39
- tags["surf_sense"] = moab_core.tag_get_handle(
40
- sense_tag_name,
41
- sense_tag_size,
42
- types.MB_TYPE_HANDLE,
43
- types.MB_TAG_SPARSE,
44
- create_if_missing=True,
45
- )
46
-
47
- tags["category"] = moab_core.tag_get_handle(
48
- types.CATEGORY_TAG_NAME,
49
- types.CATEGORY_TAG_SIZE,
50
- types.MB_TYPE_OPAQUE,
51
- types.MB_TAG_SPARSE,
52
- create_if_missing=True,
53
- )
54
-
55
- tags["name"] = moab_core.tag_get_handle(
56
- types.NAME_TAG_NAME,
57
- types.NAME_TAG_SIZE,
58
- types.MB_TYPE_OPAQUE,
59
- types.MB_TAG_SPARSE,
60
- create_if_missing=True,
61
- )
62
-
63
- tags["geom_dimension"] = moab_core.tag_get_handle(
64
- types.GEOM_DIMENSION_TAG_NAME,
65
- 1,
66
- types.MB_TYPE_INTEGER,
67
- types.MB_TAG_DENSE,
68
- create_if_missing=True,
69
- )
70
-
71
- # Global ID is a default tag, just need the name to retrieve
72
- tags["global_id"] = moab_core.tag_get_handle(types.GLOBAL_ID_TAG_NAME)
73
-
74
- return moab_core, tags
75
-
76
-
77
- def prepare_moab_core_volume_set(
78
- moab_core,
79
- volume_id,
80
- tags,
81
- ):
82
- volume_set = moab_core.create_meshset()
83
-
84
- # recent versions of MOAB handle this automatically
85
- # but best to go ahead and do it manually
86
- moab_core.tag_set_data(tags["global_id"], volume_set, volume_id)
87
-
88
- # set geom IDs
89
- moab_core.tag_set_data(tags["geom_dimension"], volume_set, 3)
90
-
91
- # set category tag values
92
- moab_core.tag_set_data(tags["category"], volume_set, "Volume")
93
-
94
- return moab_core, volume_set
95
-
96
-
97
- def prepare_moab_core_surface_set(
98
- moab_core,
99
- surface_id,
100
- tags,
101
- ):
102
- surface_set = moab_core.create_meshset()
103
-
104
- moab_core.tag_set_data(tags["global_id"], surface_set, surface_id)
105
-
106
- # set geom IDs
107
- moab_core.tag_set_data(tags["geom_dimension"], surface_set, 2)
108
-
109
- # set category tag values
110
- moab_core.tag_set_data(tags["category"], surface_set, "Surface")
111
-
112
- return moab_core, surface_set
113
-
114
-
115
- def add_triangles_to_moab_core(
116
- material_tag, surface_set, moab_core, tags, triangles, moab_verts, volume_set
117
- ):
118
- for triangle in triangles:
119
- tri = (
120
- moab_verts[int(triangle[0])],
121
- moab_verts[int(triangle[1])],
122
- moab_verts[int(triangle[2])],
123
- )
124
-
125
- moab_triangle = moab_core.create_element(types.MBTRI, tri)
126
- moab_core.add_entity(surface_set, moab_triangle)
127
-
128
- group_set = moab_core.create_meshset()
129
-
130
- moab_core.tag_set_data(tags["category"], group_set, "Group")
131
-
132
- moab_core.tag_set_data(tags["name"], group_set, f"mat:{material_tag}")
133
-
134
- moab_core.tag_set_data(tags["geom_dimension"], group_set, 4)
135
-
136
- moab_core.add_entity(group_set, volume_set)
137
-
138
- return moab_core
139
-
140
-
141
- def vertices_to_h5m(
142
- vertices: Union[
143
- Iterable[Tuple[float, float, float]], Iterable["cadquery.occ_impl.geom.Vector"]
144
- ],
145
- triangles_by_solid_by_face: Iterable[Iterable[Tuple[int, int, int]]],
146
- material_tags: Iterable[str],
147
- h5m_filename="dagmc.h5m",
148
- ):
149
- """Converts vertices and triangle sets into a tagged h5m file compatible
150
- with DAGMC enabled neutronics simulations
151
-
152
- Args:
153
- vertices:
154
- triangles:
155
- material_tags:
156
- h5m_filename:
157
- """
158
-
159
- if len(material_tags) != len(triangles_by_solid_by_face):
160
- msg = f"The number of material_tags provided is {len(material_tags)} and the number of sets of triangles is {len(triangles_by_solid_by_face)}. You must provide one material_tag for every triangle set"
161
- raise ValueError(msg)
162
-
163
- # limited attribute checking to see if user passed in a list of CadQuery vectors
164
- if (
165
- hasattr(vertices[0], "x")
166
- and hasattr(vertices[0], "y")
167
- and hasattr(vertices[0], "z")
168
- ):
169
- vertices_floats = []
170
- for vert in vertices:
171
- vertices_floats.append((vert.x, vert.y, vert.z))
172
- else:
173
- vertices_floats = vertices
174
-
175
- face_ids_with_solid_ids = {}
176
- for solid_id, triangles_on_each_face in triangles_by_solid_by_face.items():
177
- for face_id, triangles_on_face in triangles_on_each_face.items():
178
- if face_id in face_ids_with_solid_ids.keys():
179
- face_ids_with_solid_ids[face_id].append(solid_id)
180
- else:
181
- face_ids_with_solid_ids[face_id] = [solid_id]
182
-
183
- moab_core, tags = _define_moab_core_and_tags()
184
-
185
- volume_sets_by_solid_id = {}
186
- for material_tag, (solid_id, triangles_on_each_face) in zip(
187
- material_tags, triangles_by_solid_by_face.items()
188
- ):
189
- volume_set = moab_core.create_meshset()
190
- volume_sets_by_solid_id[solid_id] = volume_set
191
-
192
- added_surfaces_ids = {}
193
- for material_tag, (solid_id, triangles_on_each_face) in zip(
194
- material_tags, triangles_by_solid_by_face.items()
195
- ):
196
- volume_set = volume_sets_by_solid_id[solid_id]
197
-
198
- moab_core.tag_set_data(tags["global_id"], volume_set, solid_id)
199
- moab_core.tag_set_data(tags["geom_dimension"], volume_set, 3)
200
- moab_core.tag_set_data(tags["category"], volume_set, "Volume")
201
-
202
- group_set = moab_core.create_meshset()
203
- moab_core.tag_set_data(tags["category"], group_set, "Group")
204
- moab_core.tag_set_data(tags["name"], group_set, f"mat:{material_tag}")
205
- moab_core.tag_set_data(tags["global_id"], group_set, solid_id)
206
- # moab_core.tag_set_data(tags["geom_dimension"], group_set, 4)
207
-
208
- for face_id, triangles_on_face in triangles_on_each_face.items():
209
- if face_id not in added_surfaces_ids.keys():
210
- face_set = moab_core.create_meshset()
211
- moab_core.tag_set_data(tags["global_id"], face_set, face_id)
212
- moab_core.tag_set_data(tags["geom_dimension"], face_set, 2)
213
- moab_core.tag_set_data(tags["category"], face_set, "Surface")
214
-
215
- if len(face_ids_with_solid_ids[face_id]) == 2:
216
- other_solid_id = face_ids_with_solid_ids[face_id][1]
217
- other_volume_set = volume_sets_by_solid_id[other_solid_id]
218
- sense_data = np.array(
219
- [other_volume_set, volume_set], dtype="uint64"
220
- )
221
- else:
222
- sense_data = np.array([volume_set, 0], dtype="uint64")
223
-
224
- moab_core.tag_set_data(tags["surf_sense"], face_set, sense_data)
225
-
226
- moab_verts = moab_core.create_vertices(vertices)
227
- moab_core.add_entity(face_set, moab_verts)
228
-
229
- for triangle in triangles_on_face:
230
- tri = (
231
- moab_verts[int(triangle[0])],
232
- moab_verts[int(triangle[1])],
233
- moab_verts[int(triangle[2])],
234
- )
235
-
236
- moab_triangle = moab_core.create_element(types.MBTRI, tri)
237
- moab_core.add_entity(face_set, moab_triangle)
238
-
239
- added_surfaces_ids[face_id] = face_set
240
- else:
241
- face_set = added_surfaces_ids[face_id]
242
-
243
- other_solid_id = face_ids_with_solid_ids[face_id][0]
244
-
245
- other_volume_set = volume_sets_by_solid_id[other_solid_id]
246
-
247
- sense_data = np.array([other_volume_set, volume_set], dtype="uint64")
248
- moab_core.tag_set_data(tags["surf_sense"], face_set, sense_data)
249
-
250
- moab_core.add_parent_child(volume_set, face_set)
251
-
252
- moab_core.add_entity(group_set, volume_set)
253
-
254
- all_sets = moab_core.get_entities_by_handle(0)
255
-
256
- file_set = moab_core.create_meshset()
257
-
258
- moab_core.add_entities(file_set, all_sets)
259
-
260
- moab_core.write_file(h5m_filename)
261
-
262
- return h5m_filename
@@ -1,11 +0,0 @@
1
- _version.py,sha256=3YxISZVp_DgBHizWP4UzbUATbJ2-kn6oz3xEF0uxyCg,274
2
- cad_to_dagmc/__init__.py,sha256=pI6sOn4nbEiZClP53g2UJSNgE8tkm9wz-hA_JLwUkLk,638
3
- cad_to_dagmc/brep_part_finder.py,sha256=Se5IYTbPvmYOU28C-Sx2xYrn6ZRVXWicNjra1lVyWxw,999
4
- cad_to_dagmc/brep_to_h5m.py,sha256=AXm8AF68rD6iSNpLcSyGwCPTUyXC1yoLH47Q4hmUZFc,4266
5
- cad_to_dagmc/core.py,sha256=Sd2ITQfg2pEcRjpUGZgysGJNdiXrk9En953ItqgAH7o,5584
6
- cad_to_dagmc/vertices_to_h5m.py,sha256=AUi8ddOl5pv1riH7rZY4r4L1GsgoPUO_NZ4S2sxwPJQ,8550
7
- cad_to_dagmc-0.5.0.dist-info/LICENSE,sha256=B8kznH_777JVNZ3HOKDc4Tj24F7wJ68ledaNYeL9sCw,1070
8
- cad_to_dagmc-0.5.0.dist-info/METADATA,sha256=oj692qzyRJyYR13BfyZyaoJ4Ue1QMKvbRs6U2osQ19U,4838
9
- cad_to_dagmc-0.5.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
10
- cad_to_dagmc-0.5.0.dist-info/top_level.txt,sha256=zTi8C64SEBsE5WOtPovnxhOzt-E6Oc5nC3RW6M_5aEA,22
11
- cad_to_dagmc-0.5.0.dist-info/RECORD,,