cad-to-dagmc 0.4.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
@@ -1,4 +1,16 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.4.0'
4
- __version_tuple__ = version_tuple = (0, 4, 0)
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
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,7 +13,4 @@ except PackageNotFoundError:
13
13
 
14
14
  __all__ = ["__version__"]
15
15
 
16
- from .core import CadToDagmc
17
- from .vertices_to_h5m import *
18
- from .brep_part_finder import *
19
- from .brep_to_h5m import *
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:
@@ -45,7 +397,6 @@ class CadToDagmc:
45
397
  Useful when converting the geometry to cm for use in neutronics
46
398
  simulations.
47
399
  """
48
- print(f"loading stp file {filename}")
49
400
  part = importers.importStep(str(filename)).val()
50
401
 
51
402
  if scale_factor == 1:
@@ -73,7 +424,6 @@ class CadToDagmc:
73
424
  """
74
425
 
75
426
  if isinstance(object, cq.assembly.Assembly):
76
- print("assembly found")
77
427
  object = object.toCompound()
78
428
 
79
429
  if isinstance(object, (cq.occ_impl.shapes.Compound, cq.occ_impl.shapes.Solid)):
@@ -93,23 +443,26 @@ class CadToDagmc:
93
443
  self,
94
444
  filename: str = "dagmc.h5m",
95
445
  min_mesh_size: float = 1,
96
- max_mesh_size: float = 10,
446
+ max_mesh_size: float = 5,
97
447
  mesh_algorithm: int = 1,
448
+ msh_filename: str = None,
98
449
  ):
99
450
  assembly = cq.Assembly()
100
451
  for part in self.parts:
101
452
  assembly.add(part)
102
453
 
103
- (
104
- imprinted_assembly,
105
- imprinted_solids_with_original_id,
106
- ) = cq.occ_impl.assembly.imprint(assembly)
454
+ imprinted_assembly, imprinted_solids_with_org_id = cq.occ_impl.assembly.imprint(assembly)
107
455
 
108
- original_ids = get_ids_from_assembly(assembly)
109
- scrambled_ids = get_ids_from_imprinted_assembly(
110
- 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,
111
461
  )
112
462
 
463
+ original_ids = get_ids_from_assembly(assembly)
464
+ scrambled_ids = get_ids_from_imprinted_assembly(imprinted_solids_with_org_id)
465
+
113
466
  # both id lists should be the same length as each other and the same
114
467
  # length as the self.material_tags
115
468
 
@@ -117,55 +470,10 @@ class CadToDagmc:
117
470
  original_ids, scrambled_ids, self.material_tags
118
471
  )
119
472
 
120
- gmsh, volumes = mesh_brep(
121
- brep_object=imprinted_assembly.wrapped._address(),
122
- min_mesh_size=min_mesh_size,
123
- max_mesh_size=max_mesh_size,
124
- mesh_algorithm=mesh_algorithm,
125
- )
126
-
127
473
  h5m_filename = mesh_to_h5m_in_memory_method(
128
474
  volumes=volumes,
129
475
  material_tags=material_tags_in_brep_order,
130
476
  h5m_filename=filename,
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.4.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,46 +20,53 @@ 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
24
 
25
- [![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)
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)
26
26
 
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)
27
28
 
28
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/)
29
30
 
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
+ A minimal package that uses CadQuery functionality to convert CAD geometry to [DAGMC](https://github.com/svalinn/DAGMC/) h5m files
33
33
 
34
- This particular method of producing DAGMC compatible h5m files from CAD geometry
35
- is intended to convert STP files or [CadQuery](https://cadquery.readthedocs.io) objects to h5m file.
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.
36
35
 
37
- One unique feature of this package is the ability to combine STP files with CadQuery objects.
38
- This allows for the addition of parametric geometry to static geometry.
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/).
39
37
 
40
- # Installation
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.
41
39
 
42
- In principle, any Conda distribution will work.
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)
43
41
 
42
+ # Installation prerequisite
44
43
 
45
- # Install using Conda and pip
44
+ In principle, any Conda/Mamba distribution will work. A few Conda/Mamba options are:
45
+ - [Mambaforge](https://github.com/conda-forge/miniforge#mambaforge)
46
+ - [Miniforge](https://github.com/conda-forge/miniforge#miniforge-pypy3)
47
+ - [Anaconda](https://www.anaconda.com/download)
48
+ - [Miniconda](https://docs.conda.io/en/latest/miniconda.html)
46
49
 
47
- This example uses Conda to install some dependencies that are not available via PyPi.
48
50
 
49
- Create a new conda environment
51
+ # Install using Mamba and pip
52
+
53
+ This example assumes you have installed the MambaForge option or separately
54
+ installed Mamba with ```conda install -c conda-forge mamba -y```
55
+
56
+ Create a new conda environment, I've chosen Python 3.9 here but new versions are
57
+ also supported.
50
58
  ```bash
51
- conda create --name new_env python=3.9 -y
59
+ mamba create --name new_env python=3.9 -y
52
60
  ```
53
61
 
54
62
  Activate the environment
55
63
  ```bash
56
- conda activate new_env
64
+ mamba activate new_env
57
65
  ```
58
66
 
59
67
  Install the dependencies
60
68
  ```bash
61
- conda install -c conda-forge moab multimethod typish ezdxf nptyping nlopt casadi gmsh python-gmsh ocp>=7.7.1 -y
62
- conda install -c cadquery -c conda-forge cadquery=master --no-deps -y
69
+ mamba install -c cadquery -c conda-forge moab gmsh python-gmsh cadquery=master -y
63
70
  ```
64
71
 
65
72
  Then you can install the cad_to_dagmc package with ```pip```
@@ -69,30 +76,27 @@ pip install cad_to_dagmc
69
76
 
70
77
  You may also want to install OpenMC with DAGMC to make use of the h5m geometry files produced in simulations. However you could also use other supported particle transport codes such as MCNP, FLUKA and others [link to DAGMC documentation](https://svalinn.github.io/DAGMC/).You can run ```conda install -c conda-forge openmc``` however this more specific command makes sure the latest version of OpenMC which contains DAGMC is chosen by conda / mamba
71
78
  ```bash
72
- conda install -c conda-forge -y "openmc=0.13.3=dagmc*nompi*"
79
+ mamba install -c conda-forge -y "openmc=0.13.3=dagmc*nompi*"
73
80
  ```
74
81
 
75
82
 
76
- # Install using Mamba and pip
83
+ # Install using Conda and pip
77
84
 
78
- This example uses Mamba to install some dependencies that are not available via PyPi.
85
+ This example uses Conda to install some dependencies that are not available via PyPi.
79
86
 
80
- Create a new conda environment, I've chosen Python 3.9 here but new versions are
81
- also supported.
87
+ Create a new conda environment
82
88
  ```bash
83
89
  conda create --name new_env python=3.9 -y
84
90
  ```
85
91
 
86
92
  Activate the environment
87
93
  ```bash
88
- mamba activate new_env
94
+ conda activate new_env
89
95
  ```
90
96
 
91
97
  Install the dependencies
92
98
  ```bash
93
- conda install -c conda-forge mamba -y
94
- mamba install -c conda-forge moab multimethod typish ezdxf nptyping nlopt casadi gmsh python-gmsh ocp>=7.7.1 -y
95
- mamba install -c cadquery -c conda-forge cadquery=master --no-deps -y
99
+ conda install -c cadquery -c conda-forge moab gmsh python-gmsh cadquery=master -y
96
100
  ```
97
101
 
98
102
  Then you can install the cad_to_dagmc package with ```pip```
@@ -102,9 +106,11 @@ pip install cad_to_dagmc
102
106
 
103
107
  You may also want to install OpenMC with DAGMC to make use of the h5m geometry files produced in simulations. However you could also use other supported particle transport codes such as MCNP, FLUKA and others [link to DAGMC documentation](https://svalinn.github.io/DAGMC/).You can run ```conda install -c conda-forge openmc``` however this more specific command makes sure the latest version of OpenMC which contains DAGMC is chosen by conda / mamba
104
108
  ```bash
105
- mamba install -c conda-forge -y "openmc=0.13.3=dagmc*nompi*"
109
+ conda install -c conda-forge -y "openmc=0.13.3=dagmc*nompi*"
106
110
  ```
107
111
 
112
+
113
+
108
114
  # Usage - creation of DAGMC h5m files
109
115
 
110
116
  For examples see the [examples folder](https://github.com/fusion-energy/cad_to_dagmc/tree/main/examples)
@@ -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.40.0)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,37 +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
- print(id[0])
28
- ids.append(id[0])
29
- return ids
30
-
31
-
32
- def order_material_ids_by_brep_order(original_ids, scrambled_id, material_tags):
33
- material_tags_in_brep_order = []
34
- for brep_id in scrambled_id:
35
- id_of_solid_in_org = original_ids.index(brep_id)
36
- material_tags_in_brep_order.append(material_tags[id_of_solid_in_org])
37
- return material_tags_in_brep_order
@@ -1,118 +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 = 30,
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
- ) -> str:
49
- """Converts gmsh volumes into a DAGMC h5m file.
50
-
51
- Args:
52
- volumes: the volumes in the gmsh file, found with gmsh.model.occ.importShapes
53
- material_tags: A list of material tags to tag the DAGMC volumes with.
54
- Should be in the same order as the volumes
55
- h5m_filename: the filename of the DAGMC h5m file to write
56
-
57
- Returns:
58
- The filename of the h5m file produced
59
- """
60
-
61
- if isinstance(material_tags, str):
62
- msg = f"material_tags should be a list of strings, not a single string."
63
- raise ValueError(msg)
64
-
65
- if len(volumes) != len(material_tags):
66
- msg = f"{len(volumes)} volumes found in Brep file is not equal to the number of material_tags {len(material_tags)} provided."
67
- raise ValueError(msg)
68
-
69
- n = 3 # number of verts in a triangles
70
- nodes_in_each_pg = []
71
- for dim_and_vol in volumes:
72
- # removes all groups so that the following getEntitiesForPhysicalGroup
73
- # command only finds surfaces for the volume
74
- gmsh.model.removePhysicalGroups()
75
-
76
- vol_id = dim_and_vol[1]
77
- entities_in_volume = gmsh.model.getAdjacencies(3, vol_id)
78
- surfaces_in_volume = entities_in_volume[1]
79
- ps = gmsh.model.addPhysicalGroup(2, surfaces_in_volume)
80
- gmsh.model.setPhysicalName(2, ps, f"surfaces_on_volume_{vol_id}")
81
-
82
- groups = gmsh.model.getPhysicalGroups()
83
- group = groups[0]
84
- # for group in groups:
85
- dim = group[0]
86
- tag = group[1]
87
-
88
- surfaces = gmsh.model.getEntitiesForPhysicalGroup(dim, tag)
89
-
90
- nodes_in_all_surfaces = []
91
- for surface in surfaces:
92
- _, _, nodeTags = gmsh.model.mesh.getElements(2, surface)
93
- nodeTags = nodeTags[0].tolist()
94
- shifted_node_tags = []
95
- for nodeTag in nodeTags:
96
- shifted_node_tags.append(nodeTag - 1)
97
- grouped_node_tags = [
98
- shifted_node_tags[i : i + n]
99
- for i in range(0, len(shifted_node_tags), n)
100
- ]
101
- nodes_in_all_surfaces += grouped_node_tags
102
- nodes_in_each_pg.append(nodes_in_all_surfaces)
103
-
104
- _, all_coords, _ = gmsh.model.mesh.getNodes()
105
-
106
- GroupedCoords = [
107
- all_coords[i : i + n].tolist() for i in range(0, len(all_coords), n)
108
- ]
109
-
110
- gmsh.finalize()
111
-
112
- # checks and fixes triangle fix_normals within vertices_to_h5m
113
- return vertices_to_h5m(
114
- vertices=GroupedCoords,
115
- triangles=nodes_in_each_pg,
116
- material_tags=material_tags,
117
- h5m_filename=h5m_filename,
118
- )
@@ -1,211 +0,0 @@
1
- from typing import Iterable, Tuple, Union
2
-
3
- import numpy as np
4
- import trimesh
5
- from pymoab import core, types
6
-
7
-
8
- def fix_normals(vertices, triangles_in_each_volume):
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, triangles):
16
- # for triangles in triangles_in_each_volume:
17
- mesh = trimesh.Trimesh(vertices=vertices, faces=triangles, process=False)
18
-
19
- mesh.fix_normals()
20
-
21
- return mesh.faces
22
-
23
-
24
- def _define_moab_core_and_tags() -> Tuple[core.Core, dict]:
25
- """Creates a MOAB Core instance which can be built up by adding sets of
26
- triangles to the instance
27
-
28
- Returns:
29
- (pymoab Core): A pymoab.core.Core() instance
30
- (pymoab tag_handle): A pymoab.core.tag_get_handle() instance
31
- """
32
-
33
- # create pymoab instance
34
- moab_core = core.Core()
35
-
36
- tags = dict()
37
-
38
- sense_tag_name = "GEOM_SENSE_2"
39
- sense_tag_size = 2
40
- tags["surf_sense"] = moab_core.tag_get_handle(
41
- sense_tag_name,
42
- sense_tag_size,
43
- types.MB_TYPE_HANDLE,
44
- types.MB_TAG_SPARSE,
45
- create_if_missing=True,
46
- )
47
-
48
- tags["category"] = moab_core.tag_get_handle(
49
- types.CATEGORY_TAG_NAME,
50
- types.CATEGORY_TAG_SIZE,
51
- types.MB_TYPE_OPAQUE,
52
- types.MB_TAG_SPARSE,
53
- create_if_missing=True,
54
- )
55
-
56
- tags["name"] = moab_core.tag_get_handle(
57
- types.NAME_TAG_NAME,
58
- types.NAME_TAG_SIZE,
59
- types.MB_TYPE_OPAQUE,
60
- types.MB_TAG_SPARSE,
61
- create_if_missing=True,
62
- )
63
-
64
- tags["geom_dimension"] = moab_core.tag_get_handle(
65
- types.GEOM_DIMENSION_TAG_NAME,
66
- 1,
67
- types.MB_TYPE_INTEGER,
68
- types.MB_TAG_DENSE,
69
- create_if_missing=True,
70
- )
71
-
72
- # Global ID is a default tag, just need the name to retrieve
73
- tags["global_id"] = moab_core.tag_get_handle(types.GLOBAL_ID_TAG_NAME)
74
-
75
- return moab_core, tags
76
-
77
-
78
- def prepare_moab_core(
79
- moab_core,
80
- surface_id,
81
- volume_id,
82
- tags,
83
- ):
84
- surface_set = moab_core.create_meshset()
85
- volume_set = moab_core.create_meshset()
86
-
87
- # recent versions of MOAB handle this automatically
88
- # but best to go ahead and do it manually
89
- moab_core.tag_set_data(tags["global_id"], volume_set, volume_id)
90
-
91
- moab_core.tag_set_data(tags["global_id"], surface_set, surface_id)
92
-
93
- # set geom IDs
94
- moab_core.tag_set_data(tags["geom_dimension"], volume_set, 3)
95
- moab_core.tag_set_data(tags["geom_dimension"], surface_set, 2)
96
-
97
- # set category tag values
98
- moab_core.tag_set_data(tags["category"], volume_set, "Volume")
99
- moab_core.tag_set_data(tags["category"], surface_set, "Surface")
100
-
101
- # establish parent-child relationship
102
- moab_core.add_parent_child(volume_set, surface_set)
103
-
104
- # set surface sense
105
- sense_data = [volume_set, np.uint64(0)]
106
- moab_core.tag_set_data(tags["surf_sense"], surface_set, sense_data)
107
-
108
- return moab_core, surface_set, volume_set
109
-
110
-
111
- def add_vertices_to_moab_core(moab_core, vertices, surface_set):
112
- moab_verts = moab_core.create_vertices(vertices)
113
-
114
- moab_core.add_entity(surface_set, moab_verts)
115
- return moab_core, moab_verts
116
-
117
-
118
- def add_triangles_to_moab_core(
119
- material_tag, surface_set, moab_core, tags, triangles, moab_verts, volume_set
120
- ):
121
- for triangle in triangles:
122
- tri = (
123
- moab_verts[int(triangle[0])],
124
- moab_verts[int(triangle[1])],
125
- moab_verts[int(triangle[2])],
126
- )
127
-
128
- moab_triangle = moab_core.create_element(types.MBTRI, tri)
129
- moab_core.add_entity(surface_set, moab_triangle)
130
-
131
- group_set = moab_core.create_meshset()
132
-
133
- moab_core.tag_set_data(tags["category"], group_set, "Group")
134
-
135
- moab_core.tag_set_data(tags["name"], group_set, f"mat:{material_tag}")
136
-
137
- moab_core.tag_set_data(tags["geom_dimension"], group_set, 4)
138
-
139
- moab_core.add_entity(group_set, volume_set)
140
-
141
- return moab_core
142
-
143
-
144
- def vertices_to_h5m(
145
- vertices: Union[
146
- Iterable[Tuple[float, float, float]], Iterable["cadquery.occ_impl.geom.Vector"]
147
- ],
148
- triangles: Iterable[Tuple[int, int, int]],
149
- material_tags: Iterable[str],
150
- h5m_filename="dagmc.h5m",
151
- ):
152
- """Converts vertices and triangle sets into a tagged h5m file compatible
153
- with DAGMC enabled neutronics simulations
154
-
155
- Args:
156
- vertices:
157
- triangles:
158
- material_tags:
159
- h5m_filename:
160
- """
161
-
162
- if len(material_tags) != len(triangles):
163
- msg = f"The number of material_tags provided is {len(material_tags)} and the number of sets of triangles is {len(triangles)}. You must provide one material_tag for every triangle set"
164
- raise ValueError(msg)
165
-
166
- # limited attribute checking to see if user passed in a list of CadQuery vectors
167
- if (
168
- hasattr(vertices[0], "x")
169
- and hasattr(vertices[0], "y")
170
- and hasattr(vertices[0], "z")
171
- ):
172
- vertices_floats = []
173
- for vert in vertices:
174
- vertices_floats.append((vert.x, vert.y, vert.z))
175
- else:
176
- vertices_floats = vertices
177
-
178
- triangles = fix_normals(
179
- vertices=vertices_floats, triangles_in_each_volume=triangles
180
- )
181
-
182
- moab_core, tags = _define_moab_core_and_tags()
183
-
184
- for vol_id, material_tag in enumerate(material_tags, 1):
185
- moab_core, surface_set, volume_set = prepare_moab_core(
186
- moab_core, surface_id=vol_id, volume_id=vol_id, tags=tags
187
- )
188
-
189
- moab_core, moab_verts = add_vertices_to_moab_core(
190
- moab_core, vertices_floats, surface_set
191
- )
192
-
193
- moab_core = add_triangles_to_moab_core(
194
- material_tag,
195
- surface_set,
196
- moab_core,
197
- tags,
198
- triangles[vol_id - 1],
199
- moab_verts,
200
- volume_set,
201
- )
202
-
203
- all_sets = moab_core.get_entities_by_handle(0)
204
-
205
- file_set = moab_core.create_meshset()
206
-
207
- moab_core.add_entities(file_set, all_sets)
208
-
209
- moab_core.write_file(h5m_filename)
210
-
211
- return h5m_filename
@@ -1,11 +0,0 @@
1
- _version.py,sha256=QiCQVGOicQZY3DBr4tYPU9egzT1VgzOJGfLt9Q26uyU,160
2
- cad_to_dagmc/__init__.py,sha256=PgkpCP8unzhcoyvw-lKpy5HLy2bnUNmFHS508_bsCCE,593
3
- cad_to_dagmc/brep_part_finder.py,sha256=by2wnjo_KrTZaihFP5m5Z_sZJSdtEef7GS4nvpwqNrc,1020
4
- cad_to_dagmc/brep_to_h5m.py,sha256=4w7xaO0uJU5c8PoQFtDoPUyzWart7pdSpynU1Rcv7xY,4107
5
- cad_to_dagmc/core.py,sha256=XNXmEn06VellMwEqTXiCKRLVPN7NRDG3X0OQZXM2cOE,5594
6
- cad_to_dagmc/vertices_to_h5m.py,sha256=h7KI2fofhBlyTMRdb0FXUO12SxW9MtnNEgwdCyNCPmk,5997
7
- cad_to_dagmc-0.4.0.dist-info/LICENSE,sha256=B8kznH_777JVNZ3HOKDc4Tj24F7wJ68ledaNYeL9sCw,1070
8
- cad_to_dagmc-0.4.0.dist-info/METADATA,sha256=9iMhi-El25ZzKZL1sKTQqwDQO5t2zd-8DEA5-acacgA,4545
9
- cad_to_dagmc-0.4.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
10
- cad_to_dagmc-0.4.0.dist-info/top_level.txt,sha256=zTi8C64SEBsE5WOtPovnxhOzt-E6Oc5nC3RW6M_5aEA,22
11
- cad_to_dagmc-0.4.0.dist-info/RECORD,,