Skip to content

Commit a076723

Browse files
author
Erik B Knudsen
authored
Merge pull request #108 from openmsr/dellabella_meshing_algo
Dellabella meshing algorithm is now available. Caveat emptor: To use it you must install ocp using conda, since the pip-package cadquery-ocp is slightly behind.
2 parents f0cd62b + 061239f commit a076723

File tree

7 files changed

+302
-75
lines changed

7 files changed

+302
-75
lines changed

.github/workflows/python-app.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ jobs:
4444
environment-name: cad_to_openmc_Testing
4545
create-args: >-
4646
python=${{matrix.pyv}}
47-
openmc=0.13.3=dagmc_mpi_openmpi*
47+
openmc=0.14.0=dagmc_mpi_openmpi*
48+
ocp=7.7.2.1
49+
cadquery=2.4.0
4850
init-shell: bash
4951
- name: Test OpenMC
5052
run: openmc --version
@@ -53,7 +55,8 @@ jobs:
5355
run: |
5456
python -m pip install --upgrade pip
5557
pip install flake8 build pytest
56-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
58+
grep -v cadq requirements.txt > reqs_cleaned.txt
59+
pip install -r reqs_cleaned.txt
5760
micromamba list
5861
python -c "import gmsh"
5962
shell: micromamba-shell {0}
@@ -67,7 +70,7 @@ jobs:
6770
- name: Build, pip-install, and test-import CAD_to_OpenMC with dependencies.
6871
run: |
6972
python -m build --sdist .
70-
pip install dist/cad_to_openmc*.tar.gz
73+
pip install --no-deps dist/cad_to_openmc*.tar.gz
7174
python -c 'import CAD_to_OpenMC.assembly'
7275
shell: micromamba-shell {0}
7376
- name: Run Tests

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# changes from 0.13.3 to x.y.z
2+
- fixing the logic of tagging. If a tag ditionary is supplied this will
3+
take precedence over extracted tags, but defaults to the extracted tag if
4+
no match is found in the dictionary
5+
- improvements to the imprinting logic, following the logic in the inbound
6+
in cadquery

src/CAD_to_OpenMC/assembly.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ def similar_solids(solid1_vol, solid1_bb, solid1_c, solid2_vol, solid2_bb, solid
180180
)
181181
return dV + dBB + dCntr
182182

183-
184183
class Assembly:
185184
"""This class encapsulates a set of geometries defined by step-files
186185
addtionally it provides access to meshing-utilities, and export to a DAGMC-enabled
@@ -207,6 +206,7 @@ def __init__(
207206
self.tags = None
208207
self.sequential_tags = None
209208
self.implicit_complement = implicit_complement
209+
self.noextract_tags = True
210210

211211
@classmethod
212212
def hdf5_in_moab(cls):
@@ -271,6 +271,7 @@ def import_stp_files(
271271
scale: overall scaling factor applied to all parts
272272
translate: Translation vector to apply to all parts in the step-file.
273273
rotate: Rotation angles to apply to the parts in the step-file.
274+
vol_skip: list of volumes to skip meshing.
274275
"""
275276
for stp in self.stp_files:
276277
warn, ct = has_degenerate_toroids(stp,True)
@@ -324,8 +325,7 @@ def import_stp_files(
324325
e.tag = tag
325326
tags_set = tags_set + 1
326327
gmsh.finalize()
327-
328-
if tags:
328+
elif tags:
329329
# tag objects according to the tags dictionary.
330330
gmsh.initialize()
331331
vols = gmsh.model.occ.importShapes(stp)
@@ -342,11 +342,17 @@ def import_stp_files(
342342
g = re.match(k, part)
343343
if g is not None:
344344
tag = tags[k]
345+
tags_set = tags_set + 1
345346
break
346347
#if tag is still not set at this point we will either leave it or set it to the default.
347348
if tag is None:
348349
if e.tag is None or self.noextract_tags:
349350
tag = self.default_tag
351+
else:
352+
#use tag from stepfile
353+
g = re.match(r"^([^\s_@]+)", part)
354+
tags_set = tags_set + 1
355+
tag=g[0]
350356
else:
351357
tag = tag
352358
if self.verbose > 1:
@@ -509,7 +515,7 @@ def solids_to_h5m(
509515
e.stl = s
510516
if self.verbose:
511517
self.print_summary()
512-
if backend == "stl2":
518+
if backend in [ "stl2", "db" ] :
513519
self.stl2h5m_byface(h5m_path.name, True)
514520
else:
515521
if heal:
@@ -630,7 +636,7 @@ def add_entities_to_moab_core(self, mbcore: core.Core, mbtags: dict, noimplicit=
630636
mbcore.tag_set_data(mbtags["category"], fset, "Surface")
631637

632638
mbcore.add_parent_child(vsets[i], fset)
633-
if len(sense) == 2:
639+
if len(sense) == 2 and sense[1]!=-1:
634640
mbcore.tag_set_data(
635641
mbtags["surf_sense"],
636642
fset,

src/CAD_to_OpenMC/assemblymesher.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@
1818
try:
1919
from .assemblymesher_cq2 import MesherCQSTL2Builder
2020
except (ImportError,OSError) as e:
21-
npcq2=e
21+
nocq2=e
2222

23-
if (nogmsh and nocq and nocq2):
23+
nodb=False
24+
try:
25+
from .assemblymesher_db import MesherDBBuilder
26+
except (ImportError,OSError) as e:
27+
nodb=e
28+
29+
if (nogmsh and nocq and nocq2 and nodb):
2430
raise ImportError("Could not import any of the mesher backends")
2531

2632
class MesherFactory(ObjectFactory):
@@ -34,3 +40,5 @@ def get(self,mesher_id,**kwargs):
3440
meshers.register_builder('stl',MesherCQSTLBuilder())
3541
if(not nocq2):
3642
meshers.register_builder('stl2',MesherCQSTL2Builder())
43+
if(not nodb):
44+
meshers.register_builder('db',MesherDBBuilder())

src/CAD_to_OpenMC/assemblymesher_cq2.py

Lines changed: 25 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,7 @@
33
import pathlib as pl
44
import OCP
55
from .assemblymesher_base import assemblymesher
6-
7-
single_thread_override = False
8-
try:
9-
import multiprocessing as mp
10-
11-
manager = mp.Manager()
12-
lock = manager.Lock()
13-
except:
14-
single_thread_override = True
6+
import os
157

168
from .stl_utils import *
179
from . import meshutils
@@ -26,6 +18,10 @@ class MesherCQSTL2(assemblymesher):
2618

2719
cq_mesher_faceHash = {}
2820

21+
#writer object from OCP
22+
wr=OCP.StlAPI.StlAPI_Writer()
23+
wr.ASCIIMode=True
24+
2925
def __init__(
3026
self,
3127
tolerance,
@@ -88,106 +84,70 @@ def _mesh_surfaces(self):
8884
# loop over all surfaces in all entities
8985
# and generate meshes (refined or otherwise)
9086
mpargs = []
91-
if single_thread_override or self.threads == 1:
92-
face_hash_table = {}
93-
else:
94-
# manager=mp.Manager()
95-
face_hash_table = manager.dict()
87+
face_hash_table={}
88+
9689
k = 0
9790
for i, e in enumerate(self.cq_mesher_entities):
9891
if self.verbosity_level:
9992
print(f"INFO: triangulating solid {i}")
10093
e.solid=self._triangulate_solid(e.solid,self.cq_mesher_tolerance,self.cq_mesher_ang_tolerance)
10194
mpargs.extend(
10295
[
103-
(k + j, j, i, self.refine, self.surface_hash(f), face_hash_table)
96+
[k + j, j, i, self.refine, self.surface_hash(f), face_hash_table]
10497
for j, f in enumerate(e.solid.Faces())
10598
]
10699
)
107100

108101
# we have a set of mesh jobs - scatter those
109-
if single_thread_override or self.threads == 1:
110-
output = []
111-
for args in mpargs:
112-
output.append(self._mesh_single_nothread(*args))
113-
else:
114-
pool = mp.Pool(processes=self.threads)
115-
output = pool.starmap(self._mesh_single, mpargs)
102+
for args in mpargs:
103+
self._mesh_single(*args)
116104

117105
# process the list of meshed faces.
118106
stls = []
119107
for i, e in enumerate(self.cq_mesher_entities):
120108
face_stls = []
121109
for k, v in face_hash_table.items():
122-
vids = v[1] # the volumes that this face belongs to
110+
vids = v[1:] # the volumes that this face belongs to
123111
if i in vids:
124112
# this face is part of this volume
125-
face_stls.append(v)
113+
face_stls.append([v[0],v[1:]])
126114
stls.append(face_stls)
127115
return stls
128116

129117
def _triangulate_solid(self, solid, tol: float = 1e-3, atol: float = 1e-1):
130118
""" create a mesh by means of the underlying OCCT IncrementalMesh
131119
on a single solid. This will later be split into surfaces.
132120
This has to be done since otherwise a single solid can get leaky
133-
when surfaces do not connect
121+
when its surfaces do not connect properly
134122
"""
135123
solid.mesh(tol,atol)
136124
return solid
137125

138126
@classmethod
139-
def _mesh_single_nothread(cls, global_fid, fid, vid, refine, hh, faceHash):
127+
def _mesh_single(cls, global_fid, fid, vid, refine, hh, faceHash):
140128
f = cls.cq_mesher_entities[vid].solid.Faces()[fid]
141129
if hh in faceHash.keys():
142130
# surface is in table - simply add the vid to the hash-table
143-
faceHash[hh][1].append(vid)
144-
if cls.verbosity_level:
145-
print(f"INFO: mesher reusing {hh} ({faceHash[hh][0]},{faceHash[hh][1]})")
146-
return (hh, faceHash[hh])
131+
done=True
132+
ffn=faceHash[hh][0]
133+
previd=faceHash[hh][1]
134+
faceHash[hh]=[ffn,previd,vid]
147135
else:
148-
facefilename = f"vol_{vid+1}_face{global_fid:04}.stl"
149-
wr=OCP.StlAPI.StlAPI_Writer()
150-
wr.ASCIIMode=True
151-
status=False
152-
status=wr.Write(f.wrapped,facefilename)
153-
k=0
154-
while (not status):
155-
print(f'WARNING: failed to write file {facefilename}, retrying (iter{k})')
156-
status=wr.Write(f.wrapped,facefilename)
157-
k=k+1
158-
if(k>8):
159-
print(f'ERROR: could not write file {facefilename}, volume {vid+1} will likely be leaking')
160-
return None
161-
faceHash[hh] = [facefilename, manager.list([vid])]
162-
if cls.verbosity_level > 1:
163-
print(f"INFO: cq export to file {facefilename}")
164-
if refine:
165-
cls._refine_stls(facefilename, refine)
166-
return (hh, faceHash[hh])
136+
done=False
167137

168-
@classmethod
169-
def _mesh_single(cls, global_fid, fid, vid, refine, hh, faceHash):
170-
f = cls.cq_mesher_entities[vid].solid.Faces()[fid]
171-
if hh in faceHash.keys():
172-
# surface is in table - simply add the vid to the hash-table
173-
with lock:
174-
faceHash[hh][1].append(vid)
138+
if done:
175139
if cls.verbosity_level:
176-
print(f"INFO: mesher reusing {hh} {faceHash[hh][1]}")
177-
return (hh, faceHash[hh])
140+
print(f"INFO: mesher reusing {hh} ({faceHash[hh][0]},{faceHash[hh][1:]})")
141+
return
178142
else:
179143
facefilename = f"vol_{vid+1}_face{global_fid:04}.stl"
180-
with lock:
181-
faceHash[hh] = [facefilename, manager.list([vid])]
182-
wr=OCP.StlAPI.StlAPI_Writer()
183-
wr.ASCIIMode=True
184-
wr.Write(f.wrapped,facefilename)
144+
faceHash[hh] = [facefilename, vid, -1]
145+
status=cls.wr.Write(f.wrapped,facefilename)
185146
if cls.verbosity_level > 1:
186147
print(f"INFO: cq export to file {facefilename}")
187148
if refine:
188149
cls._refine_stls(facefilename, refine)
189-
return (hh, faceHash[hh])
190-
150+
return
191151

192152
class MesherCQSTL2Builder:
193153
def __init__(self):

0 commit comments

Comments
 (0)