import typing
from numbers import Real
[docs]
class Options:
"""A class for containing conversion options
Args:
forceCylinder (bool, optional): Use cylinder (instead of cones) as
ancillary surface where unclosed torus surfaces are involved in
the solid definition. Defaults to False.
newSplitPlane (bool, optional): New method to consider plane as
cutting surface during the decomposition process. Former method
split first planes perpendicular to X,Y,Z axis and then the
other planes involved in the solid definition. New method group
all parallel planes independently whether their normal are
along X,Y,Z axes, and start the decomposition process cutting
first with the group having the highest number of parallel
planes. Defaults to True.
delLastNumber (bool, optional): Deleting the last word in the
comment if it is a number. Defaults to False.
enlargeBox (Real, optional): Enlarge box boundary when evaluating
the constraint table during the simplification of the void cell
definition. (unit is millimeter). Defaults to 2.
nPlaneReverse (int, optional): Threshold value to determine whether
cut with parallel planes should be carried out first. Defaults
to 0.
splitTolerance (Real, optional): Fuzzy tolerance value used in the
FreeCAD function “BOPTools.SplitAPI.slice”. This function is
used during the solid decomposition process. Defaults to 0.
scaleUp (bool, optional): Scale up Fuzzy tolerance once get below
1e-12. Defaults to True.
quadricPY (bool, optional): In openMC python script format, the
cones or cylinders no aligned with the X,Y, or Z axis can be
defined using the openmc.Cone or open.Cylinder methods but can
also be defined with their quadric parameter. If “quadricPY” is
11 True then all cones and cylinders will be defined in the
openMC python script format under their quadric form. Defaults
to False.
Facets (bool, optional): use alternative conversion module when
geometry is defined by cells compound by only triangular plane
faces. Defaults to False.
prnt3PPlane (bool, optional): print 3 point plane definition in
output as 3 points coordinates. Defaults to False.
forceNoOverlap (bool, optional): force no overlaping cell
definition. Adjacent cell definition are rested from current
cell definition. Defaults to False.
"""
def __init__(
self,
forceCylinder: bool = False,
newSplitPlane: bool = True,
delLastNumber: bool = False,
enlargeBox: Real = 2.0,
nPlaneReverse: int = 0,
splitTolerance: Real = 0.0,
scaleUp: bool = True,
quadricPY: bool = False,
Facets: bool = False,
prnt3PPlane: bool = False,
forceNoOverlap: bool = False,
):
self.forceCylinder = forceCylinder
self.newSplitPlane = newSplitPlane
self.delLastNumber = delLastNumber
self.enlargeBox = enlargeBox
self.nPlaneReverse = nPlaneReverse
self.splitTolerance = splitTolerance
self.scaleUp = scaleUp
self.quadricPY = quadricPY
self.Facets = Facets
self.prnt3PPlane = prnt3PPlane
self.forceNoOverlap = forceNoOverlap
@property
def forceCylinder(self):
return self._forceCylinder
@forceCylinder.setter
def forceCylinder(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.forceCylinder should be a bool, not a {type(value)}")
self._forceCylinder = value
@property
def newSplitPlane(self):
return self._newSplitPlane
@newSplitPlane.setter
def newSplitPlane(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.newSplitPlane should be a bool, not a {type(value)}")
self._newSplitPlane = value
@property
def delLastNumber(self):
return self._delLastNumber
@delLastNumber.setter
def delLastNumber(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.delLastNumber should be a bool, not a {type(value)}")
self._delLastNumber = value
@property
def enlargeBox(self):
return self._enlargeBox
@enlargeBox.setter
def enlargeBox(self, value: Real):
if not isinstance(value, Real):
raise TypeError(f"geouned.Options.enlargeBox should be a Real, not a {type(value)}")
if value < 0:
raise ValueError(f"geouned.Options.enlargeBox should be above 0, not {value}")
self._enlargeBox = value
@property
def nPlaneReverse(self):
return self._nPlaneReverse
@nPlaneReverse.setter
def nPlaneReverse(self, value: int):
if not isinstance(value, int):
raise TypeError(f"geouned.Options.nPlaneReverse should be a int, not a {type(value)}")
self._nPlaneReverse = value
@property
def splitTolerance(self):
return self._splitTolerance
@splitTolerance.setter
def splitTolerance(self, value: Real):
if not isinstance(value, Real):
raise TypeError(f"geouned.Options.splitTolerance should be a Real, not a {type(value)}")
if value < 0:
raise ValueError(f"geouned.Options.splitTolerance should be above 0, not {value}")
self._splitTolerance = value
@property
def scaleUp(self):
return self._scaleUp
@scaleUp.setter
def scaleUp(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.scaleUp should be a bool, not a {type(value)}")
self._scaleUp = value
@property
def quadricPY(self):
return self._quadricPY
@quadricPY.setter
def quadricPY(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.quadricPY should be a bool, not a {type(value)}")
self._quadricPY = value
@property
def Facets(self):
return self._Facets
@Facets.setter
def Facets(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.Facets should be a bool, not a {type(value)}")
self._Facets = value
@property
def prnt3PPlane(self):
return self._prnt3PPlane
@prnt3PPlane.setter
def prnt3PPlane(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.prnt3PPlane should be a bool, not a {type(value)}")
self._prnt3PPlane = value
@property
def forceNoOverlap(self):
return self._forceNoOverlap
@forceNoOverlap.setter
def forceNoOverlap(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Options.forceNoOverlap should be a bool, not a {type(value)}")
self._forceNoOverlap = value
[docs]
class Tolerances:
"""A class for containing tolerances values
Args:
relativeTol (bool, optional): _description_. Defaults to False.
relativePrecision (float, optional): relative precision. Defaults to 1.0e-6.
value (float, optional): Tolerance in single value comparison. Defaults to 1.0e-6.
distance (float, optional): General Distance Tolerance. Defaults to 1.0e-4.
angle (float, optional): General Angle Tolerance. Defaults to 1.0e-4.
pln_distance (float, optional): distance between planes equal planes if distance between parallel planes < 1e-4 cm. Defaults to 1.0e-4.
pln_angle (float, optional): angle between axis. 1e-4 : planes separate each other 0.1mm each 1m. Defaults to 1.0e-4.
cyl_distance (float, optional): distance between radius/center. Defaults to 1.0e-4.
cyl_angle (float, optional): angle between axis. Defaults to 1.0e-4.
sph_distance (float, optional): distance between radius/center. Defaults to 1.0e-4.
kne_distance (float, optional): distance between apex. Defaults to 1.0e-4.
kne_angle (float, optional): angle between semiangles/axis. Defaults to 1.0e-4.
tor_distance (float, optional): distance between Major/Minor radii/center. Defaults to 1.0e-4.
tor_angle (float, optional): angle between axis. Defaults to 1.0e-4.
min_area (float, optional): minimum face area to consider in cell definition. Defaults to 1.0e-2.
"""
def __init__(
self,
relativeTol: bool = False,
relativePrecision: float = 1.0e-6,
value: float = 1.0e-6,
distance: float = 1.0e-4,
angle: float = 1.0e-4,
pln_distance: float = 1.0e-4,
pln_angle: float = 1.0e-4,
cyl_distance: float = 1.0e-4,
cyl_angle: float = 1.0e-4,
sph_distance: float = 1.0e-4,
kne_distance: float = 1.0e-4,
kne_angle: float = 1.0e-4,
tor_distance: float = 1.0e-4,
tor_angle: float = 1.0e-4,
min_area: float = 1.0e-2,
):
self.relativeTol = relativeTol
self.relativePrecision = relativePrecision
self.value = value
self.distance = distance
self.angle = angle
self.pln_distance = pln_distance
self.pln_angle = pln_angle
self.cyl_distance = cyl_distance
self.cyl_angle = cyl_angle
self.sph_distance = sph_distance
self.kne_distance = kne_distance
self.kne_angle = kne_angle
self.tor_distance = tor_distance
self.tor_angle = tor_angle
self.min_area = min_area
@property
def relativeTol(self):
return self._relativeTol
@relativeTol.setter
def relativeTol(self, value: bool):
if not isinstance(value, bool):
raise TypeError(f"geouned.Tolerances.relativeTol should be a bool, not a {type(value)}")
self._relativeTol = value
@property
def relativePrecision(self):
return self._relativePrecision
@relativePrecision.setter
def relativePrecision(self, value: float):
if not isinstance(value, float):
raise TypeError(f"geouned.Tolerances.relativePrecision should be a float, not a {type(value)}")
self._relativePrecision = value
@property
def value(self):
return self._value
@value.setter
def value(self, value: float):
if not isinstance(value, float):
raise TypeError(f"geouned.Tolerances.value should be a float, not a {type(value)}")
self._value = value
@property
def distance(self):
return self._distance
@distance.setter
def distance(self, distance: float):
if not isinstance(distance, float):
raise TypeError(f"geouned.Tolerances.distance should be a float, not a {type(distance)}")
self._distance = distance
@property
def angle(self):
return self._angle
@angle.setter
def angle(self, angle: float):
if not isinstance(angle, float):
raise TypeError(f"geouned.Tolerances.angle should be a float, not a {type(angle)}")
self._angle = angle
@property
def pln_distance(self):
return self._pln_distance
@pln_distance.setter
def pln_distance(self, pln_distance: float):
if not isinstance(pln_distance, float):
raise TypeError(f"geouned.Tolerances.pln_distance should be a float, not a {type(pln_distance)}")
self._pln_distance = pln_distance
@property
def cyl_distance(self):
return self._cyl_distance
@cyl_distance.setter
def cyl_distance(self, cyl_distance: float):
if not isinstance(cyl_distance, float):
raise TypeError(f"geouned.Tolerances.cyl_distance should be a float, not a {type(cyl_distance)}")
self._cyl_distance = cyl_distance
@property
def cyl_angle(self):
return self._cyl_angle
@cyl_angle.setter
def cyl_angle(self, cyl_angle: float):
if not isinstance(cyl_angle, float):
raise TypeError(f"geouned.Tolerances.cyl_angle should be a float, not a {type(cyl_angle)}")
self._cyl_angle = cyl_angle
@property
def sph_distance(self):
return self._sph_distance
@sph_distance.setter
def sph_distance(self, sph_distance: float):
if not isinstance(sph_distance, float):
raise TypeError(f"geouned.Tolerances.sph_distance should be a float, not a {type(sph_distance)}")
self._sph_distance = sph_distance
@property
def pln_angle(self):
return self._pln_angle
@pln_angle.setter
def pln_angle(self, pln_angle: float):
if not isinstance(pln_angle, float):
raise TypeError(f"geouned.Tolerances.pln_angle should be a float, not a {type(pln_angle)}")
self._pln_angle = pln_angle
@property
def kne_distance(self):
return self._kne_distance
@kne_distance.setter
def kne_distance(self, kne_distance: float):
if not isinstance(kne_distance, float):
raise TypeError(f"geouned.Tolerances.kne_distance should be a float, not a {type(kne_distance)}")
self._kne_distance = kne_distance
@property
def kne_angle(self):
return self._kne_angle
@kne_angle.setter
def kne_angle(self, kne_angle: float):
if not isinstance(kne_angle, float):
raise TypeError(f"geouned.Tolerances.kne_angle should be a float, not a {type(kne_angle)}")
self._kne_angle = kne_angle
@property
def tor_distance(self):
return self._tor_distance
@tor_distance.setter
def tor_distance(self, tor_distance: float):
if not isinstance(tor_distance, float):
raise TypeError(f"geouned.Tolerances.tor_distance should be a float, not a {type(tor_distance)}")
self._tor_distance = tor_distance
@property
def tor_angle(self):
return self._tor_angle
@tor_angle.setter
def tor_angle(self, tor_angle: float):
if not isinstance(tor_angle, float):
raise TypeError(f"geouned.Tolerances.tor_angle should be a float, not a {type(tor_angle)}")
self._tor_angle = tor_angle
@property
def min_area(self):
return self._min_area
@min_area.setter
def min_area(self, min_area: float):
if not isinstance(min_area, float):
raise TypeError(f"geouned.Tolerances.min_area should be a float, not a {type(min_area)}")
self._min_area = min_area
[docs]
class Settings:
"""Settings for changing the way the CAD to CSG conversion is done
Args:
matFile (str, optional): _description_. Defaults to "".
voidGen (bool, optional): Generate voids of the geometry. Defaults
to True.
debug (bool, optional): Write step files of original and decomposed
solids, for each solid in the STEP file. Defaults to False.
compSolids (bool, optional): Join subsolids of STEP file as a single
compound solid. Step files generated with SpaceClaim have not
exactly the same level of solids as FreeCAD. It may a happened
that solids defined has separated solids are read by FreeCAD
as a single compound solid (and will produce only one MCNP
cell). In this case compSolids should be set to False. Defaults
to False.
simplify (str, optional): Simplify the cell definition considering
relative surfaces position and using Boolean logics. Available
options are: "no" no optimization, "void" only void cells are
simplified. Algorithm is faster but the simplification is not
optimal. "voidfull" : only void cells are simplified with the
most optimal algorithm. The time of the conversion can be
multiplied by 5 or more. "full" : all the cells (solids and
voids) are simplified. Defaults to "No".
exportSolids (str, optional): Export CAD solid after reading.
The execution is stopped after export, the translation is not
carried out. Defaults to "".
minVoidSize (float, optional): Minimum size of the edges of the
void cell. Units are in mm. Defaults to 200.0.
maxSurf (int, optional): #TODO
maxBracket (int, optional): Maximum number of brackets (solid
complementary) allowed in void cell definition. Defaults to 30.
voidMat (list, optional): Assign a material defined by the user
instead of void for cells without material definition and the
cells generated in the automatic void generation. The format
is a 3 valued tuple (mat_label, mat_density, mat_description).
Example (100,1e-3,'Air assigned to Void'). Defaults to [].
voidExclude (list, optional): #TODO see issue 87. Defaults to [].
startCell (int, optional): Starting cell numbering label. Defaults to 1.
startSurf (int, optional): Starting surface numbering label. Defaults to 1.
sort_enclosure (bool, optional): If enclosures are defined in the
CAD models, the voids cells of the enclosure will be located in
the output file in the same location where the enclosure solid
is located in the CAD solid tree.. Defaults to False.
"""
def __init__(
self,
matFile: str = "",
voidGen: bool = True,
debug: bool = False,
compSolids: bool = False,
simplify: str = "no",
exportSolids: typing.Optional[str] = None,
minVoidSize: float = 200.0, # units mm
maxSurf: int = 50,
maxBracket: int = 30,
voidMat: list = [],
voidExclude: list = [],
startCell: int = 1,
startSurf: int = 1,
sort_enclosure: bool = False,
):
self.matFile = matFile
self.voidGen = voidGen
self.debug = debug
self.compSolids = compSolids
self.simplify = simplify
self.exportSolids = exportSolids
self.minVoidSize = minVoidSize
self.maxSurf = maxSurf
self.maxBracket = maxBracket
self.voidMat = voidMat
self.voidExclude = voidExclude
self.startCell = startCell
self.startSurf = startSurf
self.sort_enclosure = sort_enclosure
@property
def matFile(self):
return self._matFile
@matFile.setter
def matFile(self, matFile: str):
if not isinstance(matFile, str):
raise TypeError(f"geouned.Settings.matFile should be a str, not a {type(matFile)}")
self._matFile = matFile
@property
def voidGen(self):
return self._voidGen
@voidGen.setter
def voidGen(self, voidGen: bool):
if not isinstance(voidGen, bool):
raise TypeError(f"geouned.Settings.voidGen should be a bool, not a {type(voidGen)}")
self._voidGen = voidGen
@property
def debug(self):
return self._debug
@debug.setter
def debug(self, debug: bool):
if not isinstance(debug, bool):
raise TypeError(f"geouned.Settings.debug should be a bool, not a {type(debug)}")
self._debug = debug
@property
def compSolids(self):
return self._compSolids
@compSolids.setter
def compSolids(self, compSolids: bool):
if not isinstance(compSolids, bool):
raise TypeError(f"geouned.Settings.compSolids should be a bool, not a {type(compSolids)}")
self._compSolids = compSolids
@property
def simplify(self):
return self._simplify
@simplify.setter
def simplify(self, simplify: str):
if not isinstance(simplify, str):
raise TypeError(f"geouned.Settings.simplify should be a str, not a {type(simplify)}")
self._simplify = simplify
@property
def exportSolids(self):
return self._exportSolids
@exportSolids.setter
def exportSolids(self, exportSolids: str):
if exportSolids == None:
pass
elif not isinstance(exportSolids, str):
raise TypeError(f"geouned.Settings.exportSolids should be a str, not a {type(exportSolids)}")
self._exportSolids = exportSolids
@property
def minVoidSize(self):
return self._minVoidSize
@minVoidSize.setter
def minVoidSize(self, minVoidSize: float):
if not isinstance(minVoidSize, float):
raise TypeError(f"geouned.Settings.minVoidSize should be a float, not a {type(minVoidSize)}")
self._minVoidSize = minVoidSize
@property
def maxSurf(self):
return self._maxSurf
@maxSurf.setter
def maxSurf(self, maxSurf: int):
if not isinstance(maxSurf, int):
raise TypeError(f"geouned.Settings.maxSurf should be a int, not a {type(maxSurf)}")
self._maxSurf = maxSurf
@property
def maxBracket(self):
return self._maxBracket
@maxBracket.setter
def maxBracket(self, maxBracket: int):
if not isinstance(maxBracket, int):
raise TypeError(f"geouned.Settings.maxBracket should be a int, not a {type(maxBracket)}")
self._maxBracket = maxBracket
@property
def voidMat(self):
return self._voidMat
@voidMat.setter
def voidMat(self, voidMat: list):
if not isinstance(voidMat, list):
raise TypeError(f"geouned.Settings.voidMat should be a list, not a {type(voidMat)}")
if len(voidMat) == 3:
entry0, entry1, entry2 = voidMat
if not isinstance(entry0, int):
raise TypeError(f"first entry of geouned.Settings.voidMat should be an int, not a {type(entry0)}")
if not isinstance(entry1, int):
if not isinstance(entry1, float):
raise TypeError(f"second entry of geouned.Settings.voidMat should be an int or float, not a {type(entry1)}")
if not isinstance(entry2, str):
raise TypeError(f"third entry of geouned.Settings.voidMat should be a str, not a {type(entry2)}")
elif len(voidMat) > 0:
raise TypeError(f"geouned.Settings.voidMat should be a list with 3 elements or void list")
self._voidMat = voidMat
@property
def voidExclude(self):
return self._voidExclude
@voidExclude.setter
def voidExclude(self, voidExclude: list):
if not isinstance(voidExclude, list):
raise TypeError(f"geouned.Settings.voidExclude should be a list, not a {type(voidExclude)}")
for entry in voidExclude:
if not isinstance(entry, int):
raise TypeError(f"geouned.Settings.voidExclude should be a list of ints, not a {type(entry)}")
self._voidExclude = voidExclude
@property
def startCell(self):
return self._startCell
@startCell.setter
def startCell(self, startCell: int):
if not isinstance(startCell, int):
raise TypeError(f"geouned.Settings.startCell should be a int, not a {type(startCell)}")
self._startCell = startCell
@property
def startSurf(self):
return self._startSurf
@startSurf.setter
def startSurf(self, startSurf: int):
if not isinstance(startSurf, int):
raise TypeError(f"geouned.Settings.startSurf should be a int, not a {type(startSurf)}")
self._startSurf = startSurf
@property
def sort_enclosure(self):
return self._sort_enclosure
@sort_enclosure.setter
def sort_enclosure(self, sort_enclosure: bool):
if not isinstance(sort_enclosure, bool):
raise TypeError(f"geouned.Settings.sort_enclosure should be a bool, not a {type(sort_enclosure)}")
self._sort_enclosure = sort_enclosure