From 0d16df08684681e7bffa709446f1381611b62e2c Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 17 May 2024 10:27:24 +0100 Subject: [PATCH 01/27] added pypi version badge (#176) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eb80d808..25425c1b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Upload Python Package](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/psf/black) [![documentation](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml) +[![PyPI](https://img.shields.io/pypi/v/geouned?&label=PyPI)](https://pypi.org/project/geouned/) # GEOUNED From abd6dab37ee1f2754d12ea7197771fe66467d5fd Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Fri, 17 May 2024 17:31:45 +0100 Subject: [PATCH 02/27] Removing unused functions (#178) * removed two functions that are not used * removed 2nd split plane function * removed split_p_planes_org * removed gen_torus_annex_u_planes_org * removed more unused code * returned docs to dev status * format --- .../GEOUNED/conversion/cell_definition.py | 241 ------------------ src/geouned/GEOUNED/decompose/decom_one.py | 129 +--------- .../GEOUNED/utils/basic_functions_part2.py | 146 ----------- src/geouned/GEOUNED/utils/boolean_function.py | 10 +- src/geouned/GEOUNED/utils/functions.py | 50 ---- src/geouned/GEOUNED/utils/geometry_gu.py | 8 - src/geouned/GEOUNED/utils/q_form.py | 33 --- src/geouned/GEOUNED/void/void_box_class.py | 4 - src/geouned/GEOUNED/write/functions.py | 5 - src/geouned/__init__.py | 1 - 10 files changed, 2 insertions(+), 625 deletions(-) diff --git a/src/geouned/GEOUNED/conversion/cell_definition.py b/src/geouned/GEOUNED/conversion/cell_definition.py index f5d109cb..f67403cc 100644 --- a/src/geouned/GEOUNED/conversion/cell_definition.py +++ b/src/geouned/GEOUNED/conversion/cell_definition.py @@ -317,94 +317,6 @@ def gen_plane_cylinder(face, solid, tolerances): return plane -def gen_plane_cylinder_old(face, solid, tolerances): - - surf = face.Surface - rad = surf.Radius - - if str(surf) != "": - return None - - face_index = [solid.Faces.index(face)] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tolerances.min_area: - logger.warning( - f"surface {str(surf)} removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Radius == rad - and is_in_line(face2.Surface.Center, face.Surface.Axis, face.Surface.Center) - ): - # print 'Warning: coincident cylinder faces are the same' - face_index.append(i) - - angle_range = 0.0 - U_val = [] - for index in face_index: - Range = solid.Faces[index].ParameterRange - angle_range = angle_range + abs(Range[1] - Range[0]) - if not (Range[0] in U_val) and not (Range[1] in U_val): - U_val.append(Range[0]) - U_val.append(Range[1]) - if 2.0 * math.pi - angle_range < 1e-2: - return None - - UVNodes = [] - for index in face_index: - face2 = solid.Faces[index] - try: - UVNodes.append(face2.getUVNodes()) - except RuntimeError: - UVNodes.append(face2.getUVNodes()) - - U_val_str_cl = [] - for i, elem1 in enumerate(U_val): - num_str1 = f"{elem1:11.4E}" - if abs(elem1) < 1.0e-5: - num_str1 = "%11.4E" % 0.0 - if not (BF.is_duplicate_in_list(num_str1, i, U_val)): - U_val_str_cl.append(num_str1) - - face_index_2 = [face_index[0], face_index[0]] - - node_min = UVNodes[0][0] - node_max = UVNodes[0][1] - - dif1_0 = abs(float(U_val_str_cl[0]) - node_min[0]) - dif2_0 = abs(float(U_val_str_cl[1]) - node_max[0]) - - # searching for minimum and maximum angle points - for j, Nodes in enumerate(UVNodes): - for elem in Nodes: - dif1 = abs(float(U_val_str_cl[0]) - elem[0]) - dif2 = abs(float(U_val_str_cl[1]) - elem[0]) - - if dif1 < dif1_0: - node_min = elem - face_index_2[0] = face_index[j] - dif1_0 = dif1 - if dif2 < dif2_0: - node_max = elem - face_index_2[1] = face_index[j] - dif2_0 = dif2 - - v_1 = solid.Faces[face_index_2[0]].valueAt(node_min[0], node_min[1]) - v_2 = solid.Faces[face_index_2[1]].valueAt(node_max[0], node_max[1]) - - if v_1.isEqual(v_2, 1e-5): - logger.error("in the additional place definition") - return None - - normal = v_2.sub(v_1).cross(face.Surface.Axis) - plane = Part.Plane(v_1, normal).toShape() - - return plane - - def gen_plane_cone(face, solid, tolerances): Surf = face.Surface @@ -450,90 +362,6 @@ def gen_plane_cone(face, solid, tolerances): return plane -def gen_plane_cone_old(face, solid, tolerances): - - surf = face.Surface - if str(surf) != "": - return None - - face_index = [solid.Faces.index(face)] - - for i, face2 in enumerate(solid.Faces): - if face2.Area < tolerances.min_area: - logger.warning( - f"{str(surf)} surface removed from cell definition. Face area < Min area ({face2.Area} < {tolerances.min_area})" - ) - continue - if str(face2.Surface) == "" and not (face2.isEqual(face)): - if ( - face2.Surface.Axis.isEqual(face.Surface.Axis, 1e-5) - and face2.Surface.Apex.isEqual(face.Surface.Apex, 1e-5) - and (face2.Surface.SemiAngle - face.Surface.SemiAngle) < 1e-6 - ): - face_index.append(i) - - angle_range = 0.0 - u_val = [] - for index in face_index: - parameter_range = solid.Faces[index].ParameterRange - angle_range = angle_range + abs(parameter_range[1] - parameter_range[0]) - u_val.append(parameter_range[0]) - u_val.append(parameter_range[1]) - - if 2.0 * math.pi - angle_range < 1e-2: - return None - - uv_nodes = [] - for index in face_index: - face2 = solid.Faces[index] - try: - uv_nodes.append(face2.getUVNodes()) - except RuntimeError: - face.tessellate(1.0, True) - uv_nodes.append(face2.getUVNodes()) - - u_val_str_cl = [] - - for i, elem1 in enumerate(u_val): - num_str1 = f"{elem1:11.4E}" - if abs(elem1) < 1.0e-5: - num_str1 = "%11.4E" % 0.0 - if not (BF.is_duplicate_in_list(num_str1, i, u_val)): - u_val_str_cl.append(num_str1) - - face_index_2 = [face_index[0], face_index[0]] - - node_min = uv_nodes[0][0] - node_max = uv_nodes[0][1] - dif1_0 = abs(float(u_val_str_cl[0]) - node_min[0]) - dif2_0 = abs(float(u_val_str_cl[1]) - node_max[0]) - - # searching for minimum and maximum angle points - for j, Nodes in enumerate(uv_nodes): - for elem in Nodes: - dif1 = abs(float(u_val_str_cl[0]) - elem[0]) - dif2 = abs(float(u_val_str_cl[1]) - elem[0]) - if dif1 < dif1_0: - node_min = elem - face_index_2[0] = face_index[j] - dif1_0 = dif1 - if dif2 < dif2_0: - node_max = elem - face_index_2[1] = face_index[j] - dif2_0 = dif2 - - v_1 = solid.Faces[face_index_2[0]].valueAt(node_min[0], node_min[1]) - v_2 = solid.Faces[face_index_2[1]].valueAt(node_max[0], node_max[1]) - - if v_1.isEqual(v_2, 1e-5): - logger.error("in the additional place definition") - return None - - plane = Part.Plane(v_1, v_2, face.Surface.Apex).toShape() - - return plane - - def gen_torus_annex_u_planes(face, u_params, tolerances): if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): @@ -585,50 +413,6 @@ def gen_torus_annex_u_planes(face, u_params, tolerances): ), True # (d1 : d2) -def gen_torus_annex_u_planes_org(face, u_params, tolerances): - - if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): - axis = FreeCAD.Vector(1, 0, 0) - elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 1, 0), tolerances.tor_angle): - axis = FreeCAD.Vector(0, 1, 0) - elif is_parallel(face.Surface.Axis, FreeCAD.Vector(0, 0, 1), tolerances.tor_angle): - axis = FreeCAD.Vector(0, 0, 1) - - center = face.Surface.Center - p1 = face.valueAt(u_params[0], 0.0) - p2 = face.valueAt(u_params[1], 0.0) - pmid = face.valueAt(0.5 * (u_params[0] + u_params[1]), 0.0) - - if is_same_value(abs(u_params[1] - u_params[0]), math.pi, tolerances.value): - d = axis.cross(p2 - p1) - d.normalize() - if pmid.dot(d) < 0: - d = -d - return ((center, d, face.Surface.MajorRadius), None), False - - else: - d1 = axis.cross(p1) - d1.normalize() - if pmid.dot(d1) < 0: - d1 = -d1 - - d2 = axis.cross(p2) - d2.normalize() - if pmid.dot(d2) < 0: - d2 = -d2 - - if u_params[1] - u_params[0] < math.pi: - return ( - (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), - (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), - ), False # ( d1 d2 ) - else: - return ( - (center, d1, face.Surface.MajorRadius, face.Surface.MajorRadius), - (center, d2, face.Surface.MajorRadius, face.Surface.MajorRadius), - ), True # (d1 : d2) - - def gen_torus_annex_v_surface(face, v_params, tolerances, force_cylinder=False): if is_parallel(face.Surface.Axis, FreeCAD.Vector(1, 0, 0), tolerances.tor_angle): axis = FreeCAD.Vector(1, 0, 0) @@ -1102,31 +886,6 @@ def no_overlapping_cell(metaList, surfaces, options): m.Definition.level_update() -# TODO this function looks like it is not used in the code. -def extra_plane_cyl_face(face, box, surfaces, options, tolerances, numeric_format): - wire = face.OuterWire - planes_id = [] - for e in wire.OrderedEdges: - curve = str(e.Curve) - if curve[0:6] == "Circle" or curve == "": - dir = e.Curve.Axis - center = e.Curve.Center - if curve == "": - dim1 = e.Curve.MinorRadius - dim2 = e.Curve.MajorRadius - else: - dim1 = e.Curve.Radius - dim2 = e.Curve.Radius - plane = GeounedSurface(("Plane", (center, dir, dim1, dim2)), box, Face="Build") - id, exist = surfaces.add_plane(plane, options, tolerances, numeric_format, False) - if exist: - pp = surfaces.get_surface(id) - if is_opposite(plane.Surf.Axis, pp.Surf.Axis, tolerances.pln_angle): - id = -id - planes_id.append(id) - return planes_id - - def add_cone_plane(definition, cones_list, surfaces, universe_box, options, tolerances, numeric_format): x_axis = FreeCAD.Vector(1, 0, 0) y_axis = FreeCAD.Vector(0, 1, 0) diff --git a/src/geouned/GEOUNED/decompose/decom_one.py b/src/geouned/GEOUNED/decompose/decom_one.py index 9b2064e3..6853b5d5 100644 --- a/src/geouned/GEOUNED/decompose/decom_one.py +++ b/src/geouned/GEOUNED/decompose/decom_one.py @@ -331,22 +331,7 @@ def extract_surfaces(solid, kind, universe_box, options, tolerances, numeric_for return surfaces -# TODO check if this function is used as it appears to be not used elsewhere in the src folder -def is_already_in_planes(plane, Array): - - for elem in Array: - if plane.Surface.Axis.cross(elem.Surface.Axis) == FreeCAD.Vector(0, 0, 0) and is_in_plane( - plane.Surface.Position, elem.Surface - ): - return True - - return False - - -# -# # Check if to faces are joint -# def contiguous_face(face1, face2, tolerances): return face1.distToShape(face2)[0] < tolerances.distance @@ -677,14 +662,7 @@ def plane_2nd_order(solid_GU, face, flag_inv, tolerances, convex=True): return planes -def split_planes(Solids, universe_box, options, tolerances, numeric_format, newVersion=True): - if newVersion: - return split_planes_new(Solids, universe_box, options, tolerances, numeric_format) - else: - return split_planes_org(Solids, universe_box, options, tolerances, numeric_format) - - -def split_planes_new(Solids, universe_box, options, tolerances, numeric_format): +def split_planes(Solids, universe_box, options, tolerances, numeric_format): Bases = Solids[:] simpleSolid = [] @@ -705,96 +683,6 @@ def split_planes_new(Solids, universe_box, options, tolerances, numeric_format): return simpleSolid, 0 -def split_planes_org(Solids, universe_box, options, tolerances, numeric_format): - Bases = [] - err = 0 - for sol in Solids: - Bases.append((sol, [])) - - # Loop 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 - # order x y z x z y y x z y z x z x y z y x - # index 0 0 0 0 1 0 1 0 0 1 1 0 2 0 0 2 1 0 - - # planes orthogonal to X,Y,Z axis - for iplane in range(3): - newBase = [] - for item in Bases: - - base = item[0] - index = item[1] - SPlanes = extract_surfaces( - base, - "Planes", - universe_box, - options, - tolerances, - numeric_format, - MakeObj=True, - ) - Planes = [SPlanes["PX"], SPlanes["PY"], SPlanes["PZ"]] - for i in index: - del Planes[i] - pmin = len(Planes[0]) - imin = 0 - for i in range(2 - iplane): - if pmin > len(Planes[i + 1]): - pmin = len(Planes[i + 1]) - imin = i + 1 - - if len(Planes[imin]) != 0: - Tools = [] - for p in Planes[imin]: - p.build_surface() - Tools.append(p.shape) - comsolid = UF.split_bop(base, Tools, options.splitTolerance, options) - if len(comsolid.Solids) == 1: - if abs(comsolid.Solids[0].Volume - base.Volume) / base.Volume > tolerances.relativePrecision: - logger.warning( - f"Part of the split object is missing original base is used instead {abs(comsolid.Solids[0].Volume - base.Volume) / base.Volume} {comsolid.Solids[0].Volume} {base.Volume}" - ) - base.exportStep("tmp_base.stp") - s = Part.Shape() - s.read("tmp_base.stp") - solid_list = s.Solids - err = 1 - else: - solid_list = comsolid.Solids - elif len(comsolid.Solids) > 1: - solid_list = comsolid.Solids - else: - solid_list = [base] - else: - solid_list = [base] - - newindex = index[:] - newindex.append(imin) - for sol in solid_list: - newBase.append((sol, newindex)) - Bases = newBase - - XYZBases = [] - for ii, s in enumerate(Bases): - XYZBases.append(s[0]) - - simpleSolid = [] - Bases = XYZBases - - # other planes - while True: - newBases = [] - for base in Bases: - cut_solids = split_p_planes_org(base, universe_box) - if len(cut_solids) == 1: - simpleSolid.extend(cut_solids) - else: - newBases.extend(cut_solids) - if len(newBases) == 0: - break - else: - Bases = newBases - return simpleSolid, err - - class ParallelPlanes: def __init__(self, plane): self.Axis = plane.Surf.Axis @@ -875,21 +763,6 @@ def split_p_planes_new(solid, universe_box, options, tolerances, numeric_format) return out_solid -def split_p_planes_org(solid, universe_box, options, tolerances, numeric_format): - SPlanes = extract_surfaces(solid, "Planes", universe_box, options, tolerances, numeric_format, False) - - if len(SPlanes["P"]) == 0: - return [solid] - out_solid = [solid] - for p in SPlanes["P"]: - p.build_surface() - comsolid = UF.split_bop(solid, [p.shape], options.splitTolerance, options) - if len(comsolid.Solids) > 1: - out_solid = comsolid.Solids - break - return out_solid - - def split_2nd_order(Solids, universe_box, options, tolerances, numeric_format): err = 0 Base = Solids diff --git a/src/geouned/GEOUNED/utils/basic_functions_part2.py b/src/geouned/GEOUNED/utils/basic_functions_part2.py index f257ac17..853ace03 100644 --- a/src/geouned/GEOUNED/utils/basic_functions_part2.py +++ b/src/geouned/GEOUNED/utils/basic_functions_part2.py @@ -185,149 +185,3 @@ def is_duplicate_in_list(num_str1, i, lista): return True return False - - -# TODO check if this function is used -def is_in_faces(face, faces): - - if faces == []: - return False - vector_nulo = FreeCAD.Vector(0, 0, 0) - surface = face.Surface - kind_surf = str(face.Surface) - if kind_surf == "": - axis = surface.Axis - position = surface.Position - - elif kind_surf == "": - axis = surface.Axis - radius = surface.Radius - center = surface.Center - - elif kind_surf == "": - axis = surface.Axis - apex = surface.Apex - semi_angle = surface.SemiAngle - - elif kind_surf[0:6] == "Sphere": - center = surface.Center - radius = surface.Radius - - elif kind_surf == "": - axis = surface.Axis - center = surface.Center - major_radius = surface.MajorRadius - minor_radius = surface.MinorRadius - - for elem in faces: - surf = elem.Surface - ##print surf - if str(surf) == "" and kind_surf == "": - vector_cross = axis.cross(surf.Axis) - if vector_cross == vector_nulo and is_in_plane(position, surf): - return True - elif str(surf) == "" and kind_surf == "": - dir = surf.Axis - rad = surf.Radius - pnt = surf.Center - vector_cross = axis.cross(surf.Axis) - if vector_cross == vector_nulo and radius == rad and is_in_line(center, dir, pnt): - return True - elif str(surf) == "" and kind_surf == "": - # corresponding logic for cone - dir = surf.Axis - punta = surf.Apex - semiangle = surf.SemiAngle - if axis.isEqual(dir, 1e-5) and apex.isEqual(punta, 1e-5) and (semi_angle - semiangle) < 1e-6: - return True - elif str(surf)[0:6] == "Sphere" and kind_surf[0:6] == "Sphere": - # corresponding logic for sphere - rad = surf.Radius - pnt = surf.Center - if center == pnt and radius == rad: - return True - - elif str(surf) == "" and kind_surf == "": - # corresponding logic for Torus - rad_maj = surf.MajorRadius - rad_min = surf.MinorRadius - pnt = surf.Center - dir = surf.Axis - if (axis.isEqual(dir, 1e-5) and center.isEqual(pnt, 1e-5) and (major_radius - rad_maj) < 1e-6) and ( - minor_radius - rad_min - ) < 1e-6: - return True - - return False - - -# TODO check if this function is used -def is_in_faces_2(face, faces): - - if faces == []: - return False - vector_nulo = FreeCAD.Vector(0, 0, 0) - surface = face - kind_surf = face.type - if kind_surf == "": - axis = surface.Axis - position = surface.Position - - elif kind_surf == "": - axis = surface.Axis - radius = surface.Radius - center = surface.Center - - elif kind_surf == "": - axis = surface.Axis - apex = surface.Apex - semi_angle = surface.SemiAngle - - elif kind_surf[0:6] == "Sphere": - center = surface.Center - radius = surface.Radius - - elif kind_surf == "": - axis = surface.Axis - center = surface.Center - major_radius = surface.MajorRadius - minor_radius = surface.MinorRadius - - for elem in faces: - ##print surf - if elem.type == "" and kind_surf == "": - vector_cross = axis.cross(elem.Axis) - if vector_cross == vector_nulo and is_in_plane(position, elem.Surface): - # if (is_parallel(elem.Axis,elem.Surface.Axis) and is_in_plane(Position,elem.Surface)): - return True - elif elem.type == "" and kind_surf == "": - dir = elem.Axis - rad = elem.Radius - pnt = elem.Center - vector_cross = axis.cross(elem.Axis) - if vector_cross == vector_nulo and radius == rad and is_in_line(center, dir, pnt): - return True - elif elem.type == "" and kind_surf == "": - # corresponding logic for cone - dir = elem.Axis - punta = elem.Apex - semiangle = elem.SemiAngle - if axis.isEqual(dir, 1e-5) and apex.isEqual(punta, 1e-5) and (semi_angle - semiangle) < 1e-6: - return True - elif elem.type == "Sphere" and kind_surf == "Sphere": - # corresponding logic for sphere - rad = elem.Radius - pnt = elem.Center - if center == pnt and radius == rad: - return True - elif elem.type == "" and kind_surf == "": - # corresponding logic for Torus - rad_maj = elem.MajorRadius - rad_min = elem.MinorRadius - pnt = elem.Center - dir = elem.Axis - if (axis.isEqual(dir, 1e-5) and center.isEqual(pnt, 1e-5) and (major_radius - rad_maj) < 1e-6) and ( - minor_radius - rad_min - ) < 1e-6: - return True - return False diff --git a/src/geouned/GEOUNED/utils/boolean_function.py b/src/geouned/GEOUNED/utils/boolean_function.py index 58650c18..2e48b1f0 100644 --- a/src/geouned/GEOUNED/utils/boolean_function.py +++ b/src/geouned/GEOUNED/utils/boolean_function.py @@ -8,16 +8,8 @@ logger = logging.getLogger("general_logger") mostinner = re.compile(r"\([^\(^\)]*\)") # identify most inner parentheses -number = re.compile(r"(?P[-+]?\d+)") # identify signed integer and record its value in mix = re.compile(r"(?P([-+]?\d+|\[0+\]))") # identify signed integer or [000...] pattern. Record the value. -TFX = re.compile(r"(?P[FTXo]+)") # identify pattern incluinding F,T,X, or o sequence ( in any order). -PValue = re.compile(r"P\d+") # identify pattern "P" + integer pattern (e.g. P3915). -NValue = re.compile(r"N\d+") # identify pattern "N" + integer pattern (e.g. N3358). -conversion = { - "T": True, - "F": False, - "X": None, -} # associate "T", "F", "X" with associated Boolean value (or None for X) +TFX = re.compile(r"(?P[FTXo]+)") # identify pattern including F,T,X, or o sequence ( in any order). class BoolSequence: diff --git a/src/geouned/GEOUNED/utils/functions.py b/src/geouned/GEOUNED/utils/functions.py index a49b1732..2fbc8a07 100644 --- a/src/geouned/GEOUNED/utils/functions.py +++ b/src/geouned/GEOUNED/utils/functions.py @@ -38,49 +38,6 @@ def get_box(comp, options): ) -def make_plane(Plane, Boxin): - - normal = Plane.Surf.Axis - p0 = Plane.Surf.Position.dot(normal) - - Box = FreeCAD.BoundBox(Boxin) - Box.enlarge(10) - - pointEdge = [] - for i in range(12): - edge = Box.getEdge(i) - p1 = normal.dot(edge[0]) - p2 = normal.dot(edge[1]) - d0 = p0 - p1 - d1 = p2 - p1 - if d1 != 0: - a = d0 / d1 - if a >= 0 and a <= 1: - pointEdge.append(edge[0] + a * (edge[1] - edge[0])) - - if len(pointEdge) == 0: - return - s = FreeCAD.Vector((0, 0, 0)) - for v in pointEdge: - s = s + v - s = s / len(pointEdge) - - vtxvec = [] - for v in pointEdge: - vtxvec.append(v - s) - - X0 = vtxvec[0] - Y0 = normal.cross(X0) - - orden = [] - for i, v in enumerate(vtxvec): - phi = np.arctan2(v.dot(Y0), v.dot(X0)) - orden.append((phi, i)) - orden.sort() - - return Part.Face(Part.makePolygon([pointEdge[p[1]] for p in orden], True)) - - class GeounedSolid: def __init__(self, id, comsolid=None): @@ -148,13 +105,6 @@ def set_cad_solid(self): self.Volume = self.CADSolid.Volume self.BoundBox = self.CADSolid.BoundBox - def set_universe_box(self, UniverseBox): - self.UniverseBox = UniverseBox - - # TODO check this function is used in the code - def set_son_enclosures(self, sonEnclosures): - self.SonEnclosures = sonEnclosures - def set_definition(self, definition, simplify=False): if definition is None: diff --git a/src/geouned/GEOUNED/utils/geometry_gu.py b/src/geouned/GEOUNED/utils/geometry_gu.py index ef0c9515..8892c94d 100644 --- a/src/geouned/GEOUNED/utils/geometry_gu.py +++ b/src/geouned/GEOUNED/utils/geometry_gu.py @@ -301,11 +301,3 @@ def define_surface(face, plane3Pts): logger.info(f"bad Surface type {kind_surf}") Surf_GU = None return Surf_GU - - -# TODO check if this function is being used as it doesn't appear to be used in the code -def list_surfaces(Surfaces): - Faces = [] - for elem in Surfaces: - Faces.extend(define_surface(face) for face in elem) - return Faces diff --git a/src/geouned/GEOUNED/utils/q_form.py b/src/geouned/GEOUNED/utils/q_form.py index 58f26edb..53fe7ada 100644 --- a/src/geouned/GEOUNED/utils/q_form.py +++ b/src/geouned/GEOUNED/utils/q_form.py @@ -46,39 +46,6 @@ def rotation_matrix(u, v): return R -# TODO check if this is being used -def rotation_matrix_angle_axis(u, angle): - """Definition of the rotation matrix for an angle and the rotation axis""" - - # defintion of the exis of rotation - - Axis = u.normalize() - - cose = math.cos(angle) - seno = math.sin(angle) - - R = FreeCAD.Matrix() - - onecos = 1.0 - cose - - # 1st row - R.A11 = cose + Axis.x**2 * onecos - R.A12 = Axis.x * Axis.y * onecos - Axis.z * seno - R.A13 = Axis.x * Axis.z * onecos + Axis.y * seno - - # 2nd row - R.A21 = Axis.x * Axis.y * onecos + Axis.z * seno - R.A22 = cose + Axis.y**2 * onecos - R.A23 = Axis.y * Axis.z * onecos - Axis.x * seno - - # 3rd row - R.A31 = Axis.z * Axis.x * onecos - Axis.y * seno - R.A32 = Axis.z * Axis.y * onecos + Axis.x * seno - R.A33 = cose + Axis.z**2 * onecos - - return R - - def q_form_cyl(Axis, Pos, rad): R = rotation_matrix(FreeCAD.Vector(1, 0, 0), Axis) diff --git a/src/geouned/GEOUNED/void/void_box_class.py b/src/geouned/GEOUNED/void/void_box_class.py index 038639c9..a5564f59 100644 --- a/src/geouned/GEOUNED/void/void_box_class.py +++ b/src/geouned/GEOUNED/void/void_box_class.py @@ -358,10 +358,6 @@ def get_void_complementary(self, Surfaces, options, tolerances, numeric_format, else: return complementary, cellIn - # TODO check this is used in the code - def get_box_number(self): - return len(self.Objects) - def get_numbers(self): ns = 0 nb = 0 diff --git a/src/geouned/GEOUNED/write/functions.py b/src/geouned/GEOUNED/write/functions.py index 7225e031..a651ca35 100644 --- a/src/geouned/GEOUNED/write/functions.py +++ b/src/geouned/GEOUNED/write/functions.py @@ -43,11 +43,6 @@ def __init__(self, linesize=80, tabspace=6): def add(self, string): self.str += string - # TODO check this is used - def del_ast_char(self): - self.str = self.str[0:-1] - self.__leftspace__ += 1 - def wrap_line(self, offset=0): self.str = self.str.strip() diff --git a/src/geouned/__init__.py b/src/geouned/__init__.py index 6b65aa64..76d53cac 100644 --- a/src/geouned/__init__.py +++ b/src/geouned/__init__.py @@ -1,4 +1,3 @@ -import logging from importlib.metadata import version # this try except attempts to import freecad (lowercase) which is the conda From 07f018f4193885fa1e51fbac684f237e68d27207 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Mon, 20 May 2024 13:14:58 +0100 Subject: [PATCH 03/27] more concise install with conda-forge (#180) * more concise install with conda-forge * added conda badges * corrected badge url --- README.md | 3 +++ docs/install/install_linux.rst | 24 ++++-------------------- docs/install/install_windows.rst | 24 ++++-------------------- 3 files changed, 11 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 25425c1b..72397ef8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ [![documentation](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml) [![PyPI](https://img.shields.io/pypi/v/geouned?&label=PyPI)](https://pypi.org/project/geouned/) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/version.svg)](https://anaconda.org/fusion-energy/geouned) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/platforms.svg)](https://anaconda.org/fusion-energy/geouned) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/downloads.svg)](https://anaconda.org/fusion-energy/geouned) # GEOUNED A tool to convert from CAD to CSG & CSG to CAD for Monte Carlo transport codes (MCNP & OpenMC). diff --git a/docs/install/install_linux.rst b/docs/install/install_linux.rst index c8dfb5db..30822452 100644 --- a/docs/install/install_linux.rst +++ b/docs/install/install_linux.rst @@ -41,19 +41,11 @@ Activate the new environment mamba activate new_env -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency - -.. code-block:: sh - - mamba install -c conda-forge freecad - - -Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. +Install GEOUNED from conda-forge .. code-block:: sh - python -m pip install geouned + mamba install -c conda-forge geouned Then you will be able to run import GEOUNED from within Python @@ -107,19 +99,11 @@ Activate the new environment conda activate new_env -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency - -.. code-block:: sh - - conda install -c conda-forge freecad - - -Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. +Install GEOUNED from conda-forge .. code-block:: sh - python -m pip install geouned + conda install -c conda-forge geouned Then you will be able to run import GEOUNED from within Python diff --git a/docs/install/install_windows.rst b/docs/install/install_windows.rst index a318a39b..b9214dc1 100644 --- a/docs/install/install_windows.rst +++ b/docs/install/install_windows.rst @@ -31,19 +31,11 @@ Activate the new environment mamba activate new_env -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency +Install GEOUNED from conda-forge .. code-block:: sh - mamba install -c conda-forge freecad - - -Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. - -.. code-block:: sh - - python -m pip install geouned + mamba install -c conda-forge geouned Then you will be able to run import GEOUNED from within Python @@ -84,19 +76,11 @@ Activate the new environment conda activate new_env -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency - -.. code-block:: sh - - conda install -c conda-forge freecad - - -Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. +Install GEOUNED from conda-forge .. code-block:: sh - python -m pip install geouned + conda install -c conda-forge geouned Then you will be able to run import GEOUNED from within Python From ac30ab8b8d4ed3c37f45ffebea9c940a554492ef Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Mon, 20 May 2024 16:09:49 +0100 Subject: [PATCH 04/27] Avoid doubled underscore in method names (#181) * renamed functions with __ * removed random commit * Revert "removed random commit" This reverts commit f35b217ad3ce2721b75b771f27dc74230a871e0a. --- src/geouned/GEOUNED/utils/geometry_gu.py | 16 ++-- src/geouned/GEOUNED/void/void_box_class.py | 10 +- src/geouned/GEOUNED/write/mcnp_format.py | 66 ++++++------- src/geouned/GEOUNED/write/openmc_format.py | 68 ++++++------- src/geouned/GEOUNED/write/phits_format.py | 100 ++++++++++---------- src/geouned/GEOUNED/write/serpent_format.py | 58 ++++++------ 6 files changed, 159 insertions(+), 159 deletions(-) diff --git a/src/geouned/GEOUNED/utils/geometry_gu.py b/src/geouned/GEOUNED/utils/geometry_gu.py index 8892c94d..4011cbe5 100644 --- a/src/geouned/GEOUNED/utils/geometry_gu.py +++ b/src/geouned/GEOUNED/utils/geometry_gu.py @@ -117,15 +117,15 @@ def __init__(self, solid, tolerances, plane3Pts=False): toroidIndex.append(i) if len(toroidIndex) != 0: - tFaces = self.__same_torus_surf__(toroidIndex) + tFaces = self.same_torus_surf(toroidIndex) for i, tSet in enumerate(tFaces): - URange = self.__merge_periodic_uv__("U", tSet) - VRange = self.__merge_periodic_uv__("V", tSet) + URange = self.merge_periodic_uv("U", tSet) + VRange = self.merge_periodic_uv("V", tSet) for t in tSet: self.TorusVParams[t] = (i, VRange) self.TorusUParams[t] = (i, URange) - def __same_torus_surf__(self, torusList): + def same_torus_surf(self, torusList): """group as a single face all the neighbour faces of the same torus""" sameTorusFace = [] temp = torusList[:] @@ -145,9 +145,9 @@ def __same_torus_surf__(self, torusList): temp.remove(c) sameTorusFace.append(current) - return self.__separate_surfaces__(sameTorusFace) + return self.separate_surfaces(sameTorusFace) - def __separate_surfaces__(self, faceList): + def separate_surfaces(self, faceList): """group all faces in faceList forming a continuous surface""" sameSurfaces = [] for tset in faceList: @@ -171,7 +171,7 @@ def __separate_surfaces__(self, faceList): return sameSurfaces # TODO check if this function is used as it appears to be nut used in the code - def __merge_no_periodic_uv__(self, parameter, faceList): + def merge_no_periodic_uv(self, parameter, faceList): if parameter == "U": i1 = 0 i2 = 2 @@ -188,7 +188,7 @@ def __merge_no_periodic_uv__(self, parameter, faceList): return mergedParams - def __merge_periodic_uv__(self, parameter, faceList): + def merge_periodic_uv(self, parameter, faceList): two_pi = 2.0 * math.pi if parameter == "U": i1 = 0 diff --git a/src/geouned/GEOUNED/void/void_box_class.py b/src/geouned/GEOUNED/void/void_box_class.py index a5564f59..b3d3ae9f 100644 --- a/src/geouned/GEOUNED/void/void_box_class.py +++ b/src/geouned/GEOUNED/void/void_box_class.py @@ -31,8 +31,8 @@ def __init__(self, MetaSolids, Enclosure): continue if m.BoundBox.isValid(): if self.BoundBox.intersect(m.BoundBox): - Obj = self.__copy_meta__(m) - self.__remove_extra_comp__(Obj, self.BoundBox) + Obj = self.copy_meta(m) + self.remove_extra_comp(Obj, self.BoundBox) self.Objects.append(Obj) return @@ -161,7 +161,7 @@ def refine(self): ) for m in self.Objects: - self.__remove_extra_comp__(m, Cube, mode="dist") + self.remove_extra_comp(m, Cube, mode="dist") return def get_void_complementary(self, Surfaces, options, tolerances, numeric_format, simplify="no"): @@ -450,7 +450,7 @@ def get_bound_planes(self): return (PXMin, PXMax, PYMin, PYMax, PZMin, PZMax) - def __remove_extra_comp__(self, Obj, Box, mode="box"): + def remove_extra_comp(self, Obj, Box, mode="box"): reducedSol = [] reducedDef = BoolSequence(operator="OR") if not Obj.Solids: @@ -476,7 +476,7 @@ def __remove_extra_comp__(self, Obj, Box, mode="box"): Obj.set_definition(reducedDef) return - def __copy_meta__(self, m): + def copy_meta(self, m): solidsCopy = m.Solids[:] facesCopy = m.Faces[:] Meta = GeounedSolid(m.__id__, solidsCopy) diff --git a/src/geouned/GEOUNED/write/mcnp_format.py b/src/geouned/GEOUNED/write/mcnp_format.py index 240026f0..5d8ef81c 100644 --- a/src/geouned/GEOUNED/write/mcnp_format.py +++ b/src/geouned/GEOUNED/write/mcnp_format.py @@ -52,10 +52,10 @@ def __init__( if isinstance(self.StepFile, (tuple, list)): self.StepFile = "; ".join(self.StepFile) - self.__get_surface_table__() - self.__simplify_planes__(Surfaces) + self.get_surface_table() + self.simplify_planes(Surfaces) - self.Surfaces = self.__sorted_surfaces__(Surfaces) + self.Surfaces = self.sorted_surfaces(Surfaces) self.Materials = set() return @@ -87,8 +87,8 @@ def write_input(self, filename): Path(filename).parent.mkdir(parents=True, exist_ok=True) logger.info(f"write MCNP file {filename}") self.inpfile = open(filename, "w", encoding="utf-8") - self.__write_header__() - self.__write_cell_block__() + self.write_header() + self.write_cell_block() self.inpfile.write(" \n") surfaceHeader = """\ @@ -97,15 +97,15 @@ def write_input(self, filename): C ########################################################## """ self.inpfile.write(surfaceHeader) - self.__write_surface_block__() + self.write_surface_block() self.inpfile.write(" \n") - self.__write_source_block__() + self.write_source_block() self.inpfile.close() return - def __write_header__(self): + def write_header(self): freeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) @@ -133,25 +133,25 @@ def __write_header__(self): self.inpfile.write(Information) return - def __write_cell_block__(self): + def write_cell_block(self): for i, cell in enumerate(self.Cells): - self.__write_cells__(cell) + self.write_cells(cell) return - def __write_surface_block__(self): + def write_surface_block(self): for surf in self.Surfaces: - self.__write_surfaces__(surf) + self.write_surfaces(surf) - def __write_cells__(self, cell): + def write_cells(self, cell): index = cell.label # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__comment_line__(cell.Comments) + comment = self.comment_line(cell.Comments) self.inpfile.write(comment) return @@ -166,14 +166,14 @@ def __write_cells__(self, cell): mcnpcell = "{}{}\n{}{}".format( cellHeader, - self.__cell_format__(cell.Definition, offset=len(cellHeader)), - self.__option_format__(cell), - self.__comment_format__(cell.Comments, cell.MatInfo), + self.cell_format(cell.Definition, offset=len(cellHeader)), + self.option_format(cell), + self.comment_format(cell.Comments, cell.MatInfo), ) self.inpfile.write(mcnpcell) return - def __write_surfaces__(self, surface): + def write_surfaces(self, surface): """Write the surfaces in MCNP format""" MCNP_def = mcnp_surface( @@ -191,7 +191,7 @@ def __write_surfaces__(self, surface): logger.info(f"Surface {surface.Type} cannot be written in MCNP input") return - def __write_source_block__(self): + def write_source_block(self): if self.SDEF_sphere is None: return @@ -214,7 +214,7 @@ def __write_source_block__(self): for line in self.SDEF_sphere: Block += line - celList, volList = self.__get_solid_cell_volume__() + celList, volList = self.get_solid_cell_volume() F4Tally = CardLine(f"F4:{self.part} ") F4Tally.extend(celList) @@ -234,10 +234,10 @@ def __write_source_block__(self): self.inpfile.write(Block) - def __cell_format__(self, Definition, offset=11): + def cell_format(self, Definition, offset=11): return write_mcnp_cell_def(Definition, tabspace=11, offset=offset) - def __option_format__(self, cell): + def option_format(self, cell): option = "" if self.Options["Volume"]: @@ -259,7 +259,7 @@ def __option_format__(self, cell): return option - def __comment_format__(self, cComment, mComment=None): + def comment_format(self, cComment, mComment=None): comment = "" if mComment: @@ -275,7 +275,7 @@ def __comment_format__(self, cComment, mComment=None): comment += f"{'':11s}${c}\n" return comment - def __comment_line__(self, lineComment): + def comment_line(self, lineComment): lineComment = lineComment.strip().split("\n") comment = "" if lineComment: @@ -286,7 +286,7 @@ def __comment_line__(self, lineComment): comment += "C \n" return comment - def __get_surface_table__(self): + def get_surface_table(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -309,33 +309,33 @@ def __get_surface_table__(self): self.surfaceTable[index] = {i} return - def __simplify_planes__(self, Surfaces): + def simplify_planes(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__change_surf_sign__(p) + self.change_surf_sign(p) if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: axis, d = points_to_coeffs(p.Surf.Points) if is_opposite(axis, p.Surf.Axis): - self.__change_surf_sign__(p) + self.change_surf_sign(p) return - def __sorted_surfaces__(self, Surfaces): + def sorted_surfaces(self, Surfaces): temp = SurfacesDict(Surfaces) surfList = [] for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): @@ -345,7 +345,7 @@ def __sorted_surfaces__(self, Surfaces): temp.del_surface(ind + 1) return surfList - def __change_surf_sign__(self, p): + def change_surf_sign(self, p): if p.Index not in self.surfaceTable.keys(): logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") @@ -357,7 +357,7 @@ def __change_surf_sign__(self, p): if s == p.Index: change_surf_sign(s, self.Cells[ic].Definition) - def __get_solid_cell_volume__(self): + def get_solid_cell_volume(self): solidList = [] volumeList = [] diff --git a/src/geouned/GEOUNED/write/openmc_format.py b/src/geouned/GEOUNED/write/openmc_format.py index af19d458..45a3eadf 100644 --- a/src/geouned/GEOUNED/write/openmc_format.py +++ b/src/geouned/GEOUNED/write/openmc_format.py @@ -22,42 +22,42 @@ def __init__(self, Meta, Surfaces, options, tolerances, numeric_format): self.tolerances = tolerances self.numeric_format = numeric_format - self.__get_surface_table__() - self.__simplify_planes__(Surfaces) + self.get_surface_table() + self.simplify_planes(Surfaces) - self.Surfaces = self.__sorted_surfaces__(Surfaces) + self.Surfaces = self.sorted_surfaces(Surfaces) self.Materials = set() def write_xml(self, filename): logger.info(f"write OpenMC xml file {filename}") Path(filename).parent.mkdir(parents=True, exist_ok=True) with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: - self.__write_xml_header__() + self.write_xml_header() self.inpfile.write("\n") - self.__write_xml_cell_block__() + self.write_xml_cell_block() self.inpfile.write(" \n") - self.__write_xml_surface_block__() + self.write_xml_surface_block() self.inpfile.write("\n") return - def __write_xml_header__(self): + def write_xml_header(self): Header = "\n" self.inpfile.write(Header) return - def __write_xml_cell_block__(self): + def write_xml_cell_block(self): for _, cell in enumerate(self.Cells): if cell.MatInfo == "Graveyard": continue - self.__write_xml_cells__(cell) + self.write_xml_cells(cell) return - def __write_xml_surface_block__(self): + def write_xml_surface_block(self): for surf in self.Surfaces[:-1]: - self.__write_xml_surfaces__(surf) - self.__write_xml_surfaces__(self.Surfaces[-1], boundary=True) + self.write_xml_surfaces(surf) + self.write_xml_surfaces(self.Surfaces[-1], boundary=True) - def __write_xml_cells__(self, cell): + def write_xml_cells(self, cell): """Write the cell in xml OpenMC format""" index = cell.label cellName = ". ".join(cell.Comments.splitlines()) @@ -78,7 +78,7 @@ def __write_xml_cells__(self, cell): self.inpfile.write(OMCcell) return - def __write_xml_surfaces__(self, surface, boundary=False): + def write_xml_surfaces(self, surface, boundary=False): """Write the surfaces in xml OpenMC format""" surfType, coeffs = open_mc_surface(surface.Type, surface.Surf, self.tolerances, self.numeric_format) @@ -101,22 +101,22 @@ def write_py(self, filename): Path(filename).parent.mkdir(parents=True, exist_ok=True) with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: - self.__write_py_header__() + self.write_py_header() if len(self.Materials) > 0: self.inpfile.write("# Materials setup\n") - self.__write_py_materials__() + self.write_py_materials() self.inpfile.write("\n") self.inpfile.write("# Surface setup\n") - self.__write_py_surface_block__() + self.write_py_surface_block() self.inpfile.write("\n") self.inpfile.write("# Cell definition \n") - self.__write_py_cell_block__() + self.write_py_cell_block() return - def __write_py_header__(self): + def write_py_header(self): Header = """\ # openMC geometry script generated by GEOUNED @@ -130,7 +130,7 @@ def __write_py_header__(self): self.inpfile.write(Header) return - def __write_py_materials__(self): + def write_py_materials(self): matList = tuple(sorted(self.Materials)) strMat = [] for m in matList: @@ -142,13 +142,13 @@ def __write_py_materials__(self): self.inpfile.write(collect) self.inpfile.write("materials.export_to_xml()\n") - def __write_py_surface_block__(self): + def write_py_surface_block(self): for surf in self.Surfaces[:-1]: - self.__write_py_surfaces__(surf) - self.__write_py_surfaces__(self.Surfaces[-1], boundary=True) + self.write_py_surfaces(surf) + self.write_py_surfaces(self.Surfaces[-1], boundary=True) - def __write_py_surfaces__(self, surface, boundary=False): + def write_py_surfaces(self, surface, boundary=False): """Write the surfaces in python OpenMC format""" surfType, coeffs = open_mc_surface( @@ -168,13 +168,13 @@ def __write_py_surfaces__(self, surface, boundary=False): self.inpfile.write(OMCsurf) return - def __write_py_cell_block__(self): + def write_py_cell_block(self): cellNames = [] for i, cell in enumerate(self.Cells): if cell.MatInfo == "Graveyard": continue - self.__write_py_cells__(cell) + self.write_py_cells(cell) if cell.__id__ is None: continue cellNames.append(f"C{cell.label}") @@ -184,7 +184,7 @@ def __write_py_cell_block__(self): self.inpfile.write(geometry) return - def __write_py_cells__(self, cell): + def write_py_cells(self, cell): """Write the cell in python OpenMC format""" index = cell.label cellName = ". ".join(cell.Comments.splitlines()) @@ -208,7 +208,7 @@ def __write_py_cells__(self, cell): self.inpfile.write(OMCcell) return - def __get_surface_table__(self): + def get_surface_table(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -231,25 +231,25 @@ def __get_surface_table__(self): self.surfaceTable[index] = {i} return - def __simplify_planes__(self, Surfaces): + def simplify_planes(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__change_surf_sign__(p) + self.change_surf_sign(p) return - def __sorted_surfaces__(self, Surfaces): + def sorted_surfaces(self, Surfaces): temp = SurfacesDict(Surfaces) surfList = [] for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): @@ -259,7 +259,7 @@ def __sorted_surfaces__(self, Surfaces): temp.del_surface(ind + 1) return surfList - def __change_surf_sign__(self, p): + def change_surf_sign(self, p): if p.Index not in self.surfaceTable.keys(): logger.info(f"{p.Type} Surface {p.Index} not used in cell definition {p.Surf.Axis} {p.Surf.Position}") diff --git a/src/geouned/GEOUNED/write/phits_format.py b/src/geouned/GEOUNED/write/phits_format.py index 98352137..08a25ee9 100644 --- a/src/geouned/GEOUNED/write/phits_format.py +++ b/src/geouned/GEOUNED/write/phits_format.py @@ -70,10 +70,10 @@ def __init__( if self.Title == "": self.Title = self.StepFile - self.__get_surface_table__() - self.__simplify_planes__(Surfaces) + self.get_surface_table() + self.simplify_planes(Surfaces) - self.Surfaces = self.__sorted_surfaces__(Surfaces) + self.Surfaces = self.sorted_surfaces(Surfaces) self.Materials = set() return @@ -82,7 +82,7 @@ def write_phits(self, filename): logger.info(f"write PHITS file {filename}") Path(filename).parent.mkdir(parents=True, exist_ok=True) with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: - self.__write_phits_header__() + self.write_phits_header() cHeader = """\ $ @@ -93,7 +93,7 @@ def write_phits(self, filename): [CELL]\n""" self.inpfile.write(cHeader) - self.__write_phits_cell_block__() + self.write_phits_cell_block() self.inpfile.write(" \n") surfaceHeader = """\ @@ -105,7 +105,7 @@ def write_phits(self, filename): [SURFACE]\n""" self.inpfile.write(surfaceHeader) - self.__write_phits_surface_block__() + self.write_phits_surface_block() self.inpfile.write(" \n") materialHeader = """\ @@ -120,7 +120,7 @@ def write_phits(self, filename): if self.DummyMat: self.inpfile.write(materialHeader) - self.__write_phits_source_block__() + self.write_phits_source_block() self.inpfile.write(" \n") volHeader = """\ @@ -136,12 +136,12 @@ def write_phits(self, filename): if self.Options["Volume"]: self.inpfile.write(volHeader) - self.__write_phits_volume_block__() + self.write_phits_volume_block() self.inpfile.write(" \n") return - def __write_phits_header__(self): + def write_phits_header(self): freeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) @@ -177,22 +177,22 @@ def __write_phits_header__(self): self.inpfile.write(Information) return - def __write_phits_cell_block__(self): + def write_phits_cell_block(self): enclenvChk = [] - enclenvChk = self.__stepfile_label_chk__(self.StepFile) + enclenvChk = self.stepfile_label_chk(self.StepFile) if enclenvChk: logger.info("Unified the inner void cell(s) definition") for i, cell in enumerate(self.Cells): - self.__write_phits_cells_uni_void_def__(cell) + self.write_phits_cells_uni_void_def(cell) return else: for i, cell in enumerate(self.Cells): - self.__write_phits_cells__(cell) + self.write_phits_cells(cell) return - def __stepfile_label_chk__(self, filename): + def stepfile_label_chk(self, filename): enclenvList = [] with open(filename) as f: @@ -203,19 +203,19 @@ def __stepfile_label_chk__(self, filename): cond2 = envelLabel == None return cond1 and cond2 - def __write_phits_surface_block__(self): + def write_phits_surface_block(self): for surf in self.Surfaces: - self.__write_phits_surfaces__(surf) + self.write_phits_surfaces(surf) - def __write_phits_cells__(self, cell): + def write_phits_cells(self, cell): index = cell.label # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__comment_line__(cell.Comments) + comment = self.comment_line(cell.Comments) self.inpfile.write(comment) return @@ -250,21 +250,21 @@ def __write_phits_cells__(self, cell): phitscell = "{}{}\n{}{}".format( cellHeader, - self.__cell_format__(cell.Definition, offset=len(cellHeader)), - self.__option_format__(cell), - self.__comment_format__(cell.Comments, cell.MatInfo), + self.cell_format(cell.Definition, offset=len(cellHeader)), + self.option_format(cell), + self.comment_format(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return - def __write_phits_cells_uni_void_def__(self, cell): + def write_phits_cells_uni_void_def(self, cell): index = cell.label # if index is None objet not contain cell definition # but a comment to insert between cells if cell.__id__ is None: - comment = self.__comment_line__(cell.Comments) + comment = self.comment_line(cell.Comments) self.inpfile.write(comment) return """ @@ -275,7 +275,7 @@ def __write_phits_cells_uni_void_def__(self, cell): # To exclude solid cell(s) from the inner void's defenition, # a string of '#(Solid Cell No.)', or inclsolidCells, # is appended to the new inner void cell definition - # after self.__cell_format__(cell.Definition) process. + # after self.cell_format(cell.Definition) process. """ if cell.Void: @@ -313,9 +313,9 @@ def __write_phits_cells_uni_void_def__(self, cell): phitscell = "{}{}\n{}{}\n".format( cellHeader, - self.__new_inner_void_def__(inclSolidCells, cell.Definition, offset=len(cellHeader)), - self.__option_format__(cell), - self.__comment_format__(cell.Comments, cell.MatInfo), + self.inner_void_def(inclSolidCells, cell.Definition, offset=len(cellHeader)), + self.option_format(cell), + self.comment_format(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return @@ -331,7 +331,7 @@ def __write_phits_cells_uni_void_def__(self, cell): # To check auto-generated voids, apply this commented out section instead # and comment out above from "if cell.Void:..." to "... else: return" # In addition, if you set volCARD = True and want for all void regions to come apperes in [VOLUME], - # comment out some part in the def __write_phits_volume_block__() section also. + # comment out some part in the def write_phits_volume_block() section also. if cell.Material == 0: logger.info(cell.IsEnclosure) if cell.MatInfo == 'Graveyard': @@ -352,14 +352,14 @@ def __write_phits_cells_uni_void_def__(self, cell): phitscell = "{}{}\n{}{}".format( cellHeader, - self.__cell_format__(cell.Definition, offset=len(cellHeader)), - self.__option_format__(cell), - self.__comment_format__(cell.Comments, cell.MatInfo), + self.cell_format(cell.Definition, offset=len(cellHeader)), + self.option_format(cell), + self.comment_format(cell.Comments, cell.MatInfo), ) self.inpfile.write(phitscell) return - def __write_phits_surfaces__(self, surface): + def write_phits_surfaces(self, surface): """Write the surfaces in PHITS format""" PHITS_def = phits_surface( @@ -377,7 +377,7 @@ def __write_phits_surfaces__(self, surface): logger.info(f"Surface {surface.Type} cannot be written in PHITS input") return - def __write_phits_source_block__(self): + def write_phits_source_block(self): if self.DummyMat: mat = list(self.Materials) @@ -402,7 +402,7 @@ def __write_phits_source_block__(self): self.inpfile.write(Block) - def __write_phits_volume_block__(self): + def write_phits_volume_block(self): vol = f"{'':5s}reg{'':5s}vol\n" @@ -410,7 +410,7 @@ def __write_phits_volume_block__(self): eliminated_endVoidIndex = self.__cells__ + self.startCell - 3 enclenvChk = [] - enclenvChk = self.__stepfile_label_chk__(self.StepFile) + enclenvChk = self.stepfile_label_chk(self.StepFile) if enclenvChk and self.Options["Volume"]: for i, cell in enumerate(self.Cells): @@ -453,17 +453,17 @@ def __write_phits_volume_block__(self): self.inpfile.write(vol) """ - def __cell_format__(self, Definition, offset=11): + def cell_format(self, Definition, offset=11): return write_phits_cell_def(Definition, tabspace=11, offset=offset) - def __new_inner_void_def__(self, innerSolidCells, Definition, offset=11): - newInnerVoidDef = self.__cell_format__(Definition, offset) + def inner_void_def(self, innerSolidCells, Definition, offset=11): + newInnerVoidDef = self.cell_format(Definition, offset) strdef = CellString(tabspace=11) strdef.add(newInnerVoidDef + innerSolidCells) strdef.wrap_line(offset) return strdef.str - def __option_format__(self, cell): + def option_format(self, cell): option = "" if self.Options["Volume"]: @@ -477,7 +477,7 @@ def __option_format__(self, cell): return option - def __comment_format__(self, cComment, mComment=None): + def comment_format(self, cComment, mComment=None): comment = "" if mComment: @@ -493,7 +493,7 @@ def __comment_format__(self, cComment, mComment=None): comment += f"{'':11s}${c}\n" return comment - def __comment_line__(self, lineComment): + def comment_line(self, lineComment): lineComment = lineComment.strip().split("\n") comment = "" if lineComment: @@ -504,7 +504,7 @@ def __comment_line__(self, lineComment): comment += "$ \n" return comment - def __get_surface_table__(self): + def get_surface_table(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -527,32 +527,32 @@ def __get_surface_table__(self): self.surfaceTable[index] = {i} return - def __simplify_planes__(self, Surfaces): + def simplify_planes(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__change_surf_sign__(p) + self.change_surf_sign(p) if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: axis, d = points_to_coeffs(p.Surf.Points) if is_opposite(axis, p.Surf.Axis): - self.__change_surf_sign__(p) + self.change_surf_sign(p) return - def __sorted_surfaces__(self, Surfaces): + def sorted_surfaces(self, Surfaces): temp = SurfacesDict(Surfaces) surfList = [] for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): @@ -562,7 +562,7 @@ def __sorted_surfaces__(self, Surfaces): temp.del_surface(ind + 1) return surfList - def __change_surf_sign__(self, p): + def change_surf_sign(self, p): if p.Index not in self.surfaceTable.keys(): logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") @@ -574,7 +574,7 @@ def __change_surf_sign__(self, p): if s == p.Index: change_surf_sign(s, self.Cells[ic].Definition) - def __get_solid_cell_volume__(self): + def get_solid_cell_volume(self): solidList = [] volumeList = [] diff --git a/src/geouned/GEOUNED/write/serpent_format.py b/src/geouned/GEOUNED/write/serpent_format.py index bb5b1eb2..f58f5741 100644 --- a/src/geouned/GEOUNED/write/serpent_format.py +++ b/src/geouned/GEOUNED/write/serpent_format.py @@ -50,10 +50,10 @@ def __init__( if isinstance(self.StepFile, (tuple, list)): self.StepFile = "; ".join(self.StepFile) - self.__get_surface_table__() - self.__simplify_planes__(Surfaces) + self.get_surface_table() + self.simplify_planes(Surfaces) - self.Surfaces = self.__sorted_surfaces__(Surfaces) + self.Surfaces = self.sorted_surfaces(Surfaces) self.Materials = set() return @@ -80,26 +80,26 @@ def write_input(self, filename): logger.info(f"write Serpent file {filename}") Path(filename).parent.mkdir(parents=True, exist_ok=True) with open(file=filename, mode="w", encoding="utf-8") as self.inpfile: - self.__write_header__() + self.write_header() cellblockHeader = """\ % --- CELL DEFINITIONS """ self.inpfile.write(cellblockHeader) - self.__write_cell_block__() + self.write_cell_block() self.inpfile.write(" \n") surfaceHeader = """\ % --- SURFACE DEFINITIONS """ self.inpfile.write(surfaceHeader) - self.__write_surface_block__() + self.write_surface_block() self.inpfile.write(" \n") - self.__write_source_block__() + self.write_source_block() return - def __write_header__(self): + def write_header(self): freeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) @@ -127,19 +127,19 @@ def __write_header__(self): self.inpfile.write(Information) return - def __write_surface_block__(self): + def write_surface_block(self): for surf in self.Surfaces: - self.__write_surfaces__(surf) + self.write_surfaces(surf) - def __write_cell_block__(self): + def write_cell_block(self): for i, cell in enumerate(self.Cells): - self.__write_cells__(cell) + self.write_cells(cell) return # to_mod - def __write_cells__(self, cell): + def write_cells(self, cell): index = cell.label # If index is None, the object does not contain cell definition @@ -161,7 +161,7 @@ def __write_cells__(self, cell): cellHeader = f'cell {index:<5d} {self.Options["Universe"]} {cell.Material:<5d} ' serpent_cell = ( - f"{cellHeader}{self.__cell_format__(cell.Definition, offset=len(cellHeader))}" + f"{cellHeader}{self.cell_format(cell.Definition, offset=len(cellHeader))}" f"{self.comment_format(cell.Comments, cell.MatInfo)}" ) self.inpfile.write(serpent_cell) @@ -175,14 +175,14 @@ def __write_cells__(self, cell): cellHeader = f"cell {index:<5d} 0 {cell.Material:<5d} " serpent_cell = ( - f"{cellHeader}{self.__cell_format__(cell.Definition, offset=len(cellHeader))}" + f"{cellHeader}{self.cell_format(cell.Definition, offset=len(cellHeader))}" f"{self.comment_format(cell.Comments, cell.MatInfo)}" ) self.inpfile.write(serpent_cell) return - def __write_surfaces__(self, surface): + def write_surfaces(self, surface): """Write the surfaces in Serpent format""" Serpent_def = serpent_surface( @@ -202,7 +202,7 @@ def __write_surfaces__(self, surface): # No void all option in Serpent. For now remove addition of source. - def __write_source_block__(self): + def write_source_block(self): # if self.SDEF_sphere is None: return MODE = f"\nset nps 1e6\nset bc 1" @@ -224,7 +224,7 @@ def __write_source_block__(self): # for line in self.SDEF_sphere: # Block += line - # celList,volList = self.__get_solid_cell_volume__() + # celList,volList = self.get_solid_cell_volume() # F4Tally = CardLine('F4:{} '.format(self.part)) # F4Tally.extend(celList) @@ -244,12 +244,12 @@ def __write_source_block__(self): self.inpfile.write(Block) - def __cell_format__(self, Definition, offset=11): + def cell_format(self, Definition, offset=11): return write_serpent_cell_def(Definition, tabspace=11, offset=offset) # Function not relevant for Serpent : No importance setting, universes assigned elsewhere. # Volumes only defined on tally cards. - # def __option_format__(self,cell): + # def option_format(self,cell): # option = '' # if self.Options['Volume']: @@ -297,7 +297,7 @@ def comment_line(self, lineComment): comment += "% \n" return comment - def __get_surface_table__(self): + def get_surface_table(self): self.surfaceTable = {} self.__solidCells__ = 0 self.__cells__ = 0 @@ -320,33 +320,33 @@ def __get_surface_table__(self): self.surfaceTable[index] = {i} return - def __simplify_planes__(self, Surfaces): + def simplify_planes(self, Surfaces): for p in Surfaces["PX"]: if p.Surf.Axis[0] < 0: p.Surf.Axis = FreeCAD.Vector(1, 0, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PY"]: if p.Surf.Axis[1] < 0: p.Surf.Axis = FreeCAD.Vector(0, 1, 0) - self.__change_surf_sign__(p) + self.change_surf_sign(p) for p in Surfaces["PZ"]: if p.Surf.Axis[2] < 0: p.Surf.Axis = FreeCAD.Vector(0, 0, 1) - self.__change_surf_sign__(p) + self.change_surf_sign(p) if self.options.prnt3PPlane: for p in Surfaces["P"]: if p.Surf.pointDef: axis, d = points_to_coeffs(p.Surf.Points) if is_opposite(axis, p.Surf.Axis): - self.__change_surf_sign__(p) + self.change_surf_sign(p) return - def __sorted_surfaces__(self, Surfaces): + def sorted_surfaces(self, Surfaces): temp = SurfacesDict(Surfaces) surfList = [] for ind in range(Surfaces.IndexOffset, Surfaces.surfaceNumber + Surfaces.IndexOffset): @@ -356,7 +356,7 @@ def __sorted_surfaces__(self, Surfaces): temp.del_surface(ind + 1) return surfList - def __change_surf_sign__(self, p): + def change_surf_sign(self, p): if p.Index not in self.surfaceTable.keys(): logger.info(f"{p.Type} Surface {p.Index} not used in cell definition) {p.Surf.Axis} {p.Surf.Position}") @@ -368,7 +368,7 @@ def __change_surf_sign__(self, p): if s == p.Index: change_surf_sign(s, self.Cells[ic].Definition) - def __get_solid_cell_volume__(self): + def get_solid_cell_volume(self): solidList = [] volumeList = [] From 5d209e075d8e26d8eeaf548ea5caa934c51bffa7 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 21 May 2024 10:17:14 +0100 Subject: [PATCH 05/27] Moving decompose solids into CadToCsg class (#183) * moving decompose solid into class * lower_case_enclosure_list_added_to_attributes --- src/geouned/GEOUNED/core.py | 233 +++++++++------------ src/geouned/GEOUNED/utils/functions.py | 2 +- src/geouned/GEOUNED/void/void_functions.py | 6 +- 3 files changed, 104 insertions(+), 137 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index 8d0399b0..abf0f3eb 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -182,7 +182,7 @@ def export_csg( write_geometry( UniverseBox=self.UniverseBox, - MetaList=self.MetaList, + MetaList=self.meta_list, Surfaces=self.Surfaces, settings=self.settings, options=self.options, @@ -464,8 +464,8 @@ def start(self): Meta, Enclosure = Load.load_cad(stp, self.settings, self.options) MetaChunk.append(Meta) EnclosureChunk.append(Enclosure) - self.MetaList = join_meta_lists(MetaChunk) - EnclosureList = join_meta_lists(EnclosureChunk) + self.meta_list: typing.List[UF.GeounedSolid] = join_meta_lists(MetaChunk) + self.enclosure_list: typing.List[UF.GeounedSolid] = join_meta_lists(EnclosureChunk) logger.info("End of loading phase") tempstr1 = str(datetime.now() - startTime) @@ -474,13 +474,13 @@ def start(self): # Select a specific solid range from original STEP solids if self.settings.cellRange: - self.MetaList = self.MetaList[self.settings.cellRange[0] : self.settings.cellRange[1]] + self.meta_list = self.meta_list[self.settings.cellRange[0] : self.settings.cellRange[1]] # export in STEP format solids read from input file # terminate excution if self.settings.exportSolids != "": solids = [] - for m in self.MetaList: + for m in self.meta_list: if m.IsEnclosure: continue solids.extend(m.Solids) @@ -489,10 +489,10 @@ def start(self): raise ValueError(msg) # set up Universe - if EnclosureList: - self.UniverseBox = get_universe(self.MetaList + EnclosureList) + if self.enclosure_list: + self.UniverseBox = get_universe(self.meta_list + self.enclosure_list) else: - self.UniverseBox = get_universe(self.MetaList) + self.UniverseBox = get_universe(self.meta_list) self.Surfaces = UF.SurfacesDict(offset=self.settings.startSurf - 1) @@ -503,37 +503,17 @@ def start(self): if not self.options.Facets: # decompose all solids in elementary solids (convex ones) - warningSolidList = decompose_solids( - self.MetaList, - self.Surfaces, - self.UniverseBox, - self.settings, - True, - self.options, - self.tolerances, - self.numeric_format, - "Decomposing solids", - ) + warningSolidList = self._decompose_solids(meta=True) # decompose Enclosure solids - if self.settings.voidGen and EnclosureList: - warningEnclosureList = decompose_solids( - EnclosureList, - self.Surfaces, - self.UniverseBox, - self.settings, - False, - self.options, - self.tolerances, - self.numeric_format, - "Decomposing enclosure solids", - ) + if self.settings.voidGen and self.enclosure_list: + warningEnclosureList = self._decompose_solids(meta=False) logger.info("End of decomposition phase") # start Building CGS cells phase - for j, m in enumerate(tqdm(self.MetaList, desc="Translating solid cells")): + for j, m in enumerate(tqdm(self.meta_list, desc="Translating solid cells")): if m.IsEnclosure: continue logger.info(f"Building cell: {j+1}") @@ -554,11 +534,11 @@ def start(self): logger.info(m.Definition) if self.options.forceNoOverlap: - Conv.no_overlapping_cell(self.MetaList, self.Surfaces, self.options) + Conv.no_overlapping_cell(self.meta_list, self.Surfaces, self.options) else: translate( - self.MetaList, + self.meta_list, self.Surfaces, self.UniverseBox, self.settings, @@ -566,26 +546,16 @@ def start(self): self.tolerances, ) # decompose Enclosure solids - if self.settings.voidGen and EnclosureList: - warningEnclosureList = decompose_solids( - EnclosureList, - self.Surfaces, - self.UniverseBox, - self.settings, - False, - self.options, - self.tolerances, - self.numeric_format, - "Decomposing enclosure solids", - ) + if self.settings.voidGen and self.enclosure_list: + warningEnclosureList = self._decompose_solids(meta=False) tempstr2 = str(datetime.now() - tempTime) logger.info(tempstr2) # building enclosure solids - if self.settings.voidGen and EnclosureList: - for j, m in enumerate(EnclosureList): + if self.settings.voidGen and self.enclosure_list: + for j, m in enumerate(self.enclosure_list): logger.info(f"Building Enclosure Cell: {j + 1}") cones = Conv.cellDef( m, @@ -603,22 +573,22 @@ def start(self): tempTime1 = datetime.now() # void generation phase - MetaVoid = [] + meta_void = [] if self.settings.voidGen: logger.info("Build Void") logger.info(self.settings.voidExclude) if not self.settings.voidExclude: - MetaReduced = self.MetaList + meta_reduced = self.meta_list else: - MetaReduced = exclude_cells(self.MetaList, self.settings.voidExclude) + meta_reduced = exclude_cells(self.meta_list, self.settings.voidExclude) - if self.MetaList: - init = self.MetaList[-1].__id__ - len(EnclosureList) + if self.meta_list: + init = self.meta_list[-1].__id__ - len(self.enclosure_list) else: init = 0 - MetaVoid = void.void_generation( - MetaReduced, - EnclosureList, + meta_void = void.void_generation( + meta_reduced, + self.enclosure_list, self.Surfaces, self.UniverseBox, self.settings, @@ -635,7 +605,7 @@ def start(self): for s in lst: Surfs[s.Index] = s - for c in tqdm(self.MetaList, desc="Simplifying"): + for c in tqdm(self.meta_list, desc="Simplifying"): if c.Definition.level == 0 or c.IsEnclosure: continue logger.info(f"simplify cell {c.__id__}") @@ -652,18 +622,18 @@ def start(self): logger.info(datetime.now() - startTime) cellOffSet = self.settings.startCell - 1 - if EnclosureList and self.settings.sort_enclosure: + if self.enclosure_list and self.settings.sort_enclosure: # sort group solid cell / void cell sequence in each for each enclosure # if a solid belong to several enclosure, its definition will be written # for the highest enclosure level or if same enclosure level in the first # enclosure found - self.MetaList = sort_enclosure(self.MetaList, MetaVoid, cellOffSet) + self.meta_list = sort_enclosure(self.meta_list, meta_void, cellOffSet) else: # remove Null Cell and apply cell numbering offset deleted = [] idLabel = {0: 0} icount = cellOffSet - for i, m in enumerate(self.MetaList): + for i, m in enumerate(self.meta_list): if m.NullCell or m.IsEnclosure: deleted.append(i) continue @@ -673,7 +643,7 @@ def start(self): idLabel[m.__id__] = m.label for i in reversed(deleted): - del self.MetaList[i] + del self.meta_list[i] lineComment = """\ ########################################################## @@ -681,10 +651,10 @@ def start(self): ##########################################################""" mc = UF.GeounedSolid(None) mc.Comments = lineComment - self.MetaList.append(mc) + self.meta_list.append(mc) deleted = [] - for i, m in enumerate(MetaVoid): + for i, m in enumerate(meta_void): if m.NullCell: deleted.append(i) continue @@ -692,15 +662,15 @@ def start(self): m.label = icount update_comment(m, idLabel) for i in reversed(deleted): - del MetaVoid[i] + del meta_void[i] - self.MetaList.extend(MetaVoid) + self.meta_list.extend(meta_void) print_warning_solids(warnSolids, warnEnclosures) # add plane definition to cone process_cones( - self.MetaList, + self.meta_list, coneInfo, self.Surfaces, self.UniverseBox, @@ -715,76 +685,73 @@ def start(self): logger.info(f"Translation time of solid cells {tempTime1} - {tempTime0}") logger.info(f"Translation time of void cells {tempTime2} - {tempTime1}") + def _decompose_solids(self, meta: bool): -def decompose_solids( - MetaList, - Surfaces, - UniverseBox, - setting, - meta, - options, - tolerances, - numeric_format, - description, -): - totsolid = len(MetaList) - warningSolids = [] - for i, m in enumerate(tqdm(MetaList, desc=description)): - if meta and m.IsEnclosure: - continue - logger.info(f"Decomposing solid: {i + 1}/{totsolid}") - if setting.debug: - logger.info(m.Comments) - if not path.exists("debug"): - mkdir("debug") - if m.IsEnclosure: - m.Solids[0].exportStep(f"debug/origEnclosure_{i}.stp") - else: - m.Solids[0].exportStep(f"debug/origSolid_{i}.stp") - - comsolid, err = Decom.SplitSolid( - Part.makeCompound(m.Solids), - UniverseBox, - options, - tolerances, - numeric_format, - ) + if meta: + meta_list = self.meta_list + description = "Decomposing solids" + else: + meta_list = self.enclosure_list + description = "Decomposing enclosure solids" - if err != 0: - if not path.exists("Suspicious_solids"): - mkdir("Suspicious_solids") - if m.IsEnclosure: - Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Enclosure_original_{i}.stp") - comsolid.exportStep(f"Suspicious_solids/Enclosure_split_{i}.stp") - else: - Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Solid_original_{i}.stp") - comsolid.exportStep(f"Suspicious_solids/Solid_split_{i}.stp") + totsolid = len(meta_list) + warningSolids = [] + for i, m in enumerate(tqdm(meta_list, desc=description)): + if meta and m.IsEnclosure: + continue + logger.info(f"Decomposing solid: {i + 1}/{totsolid}") + if self.settings.debug: + logger.info(m.Comments) + if not path.exists("debug"): + mkdir("debug") + if m.IsEnclosure: + m.Solids[0].exportStep(f"debug/origEnclosure_{i}.stp") + else: + m.Solids[0].exportStep(f"debug/origSolid_{i}.stp") - warningSolids.append(i) + comsolid, err = Decom.SplitSolid( + Part.makeCompound(m.Solids), + self.UniverseBox, + self.options, + self.tolerances, + self.numeric_format, + ) - if setting.debug: - if m.IsEnclosure: - comsolid.exportStep(f"debug/compEnclosure_{i}.stp") - else: - comsolid.exportStep(f"debug/compSolid_{i}.stp") - Surfaces.extend( - Decom.extract_surfaces( - comsolid, - "All", - UniverseBox, - options, - tolerances, - numeric_format, - MakeObj=True, - ), - options, - tolerances, - numeric_format, - ) - m.set_cad_solid() - m.update_solids(comsolid.Solids) + if err != 0: + if not path.exists("Suspicious_solids"): + mkdir("Suspicious_solids") + if m.IsEnclosure: + Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Enclosure_original_{i}.stp") + comsolid.exportStep(f"Suspicious_solids/Enclosure_split_{i}.stp") + else: + Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Solid_original_{i}.stp") + comsolid.exportStep(f"Suspicious_solids/Solid_split_{i}.stp") + + warningSolids.append(i) + + if self.settings.debug: + if m.IsEnclosure: + comsolid.exportStep(f"debug/compEnclosure_{i}.stp") + else: + comsolid.exportStep(f"debug/compSolid_{i}.stp") + self.Surfaces.extend( + Decom.extract_surfaces( + comsolid, + "All", + self.UniverseBox, + self.options, + self.tolerances, + self.numeric_format, + MakeObj=True, + ), + self.options, + self.tolerances, + self.numeric_format, + ) + m.set_cad_solid() + m.update_solids(comsolid.Solids) - return warningSolids + return warningSolids def update_comment(meta, idLabel): @@ -913,10 +880,10 @@ def exclude_cells(MetaList, labelList): return voidMeta -def sort_enclosure(MetaList, MetaVoid, offSet=0): +def sort_enclosure(MetaList, meta_void, offSet=0): newList = {} - for m in MetaVoid: + for m in meta_void: if m.EnclosureID in newList.keys(): newList[m.EnclosureID].append(m) else: diff --git a/src/geouned/GEOUNED/utils/functions.py b/src/geouned/GEOUNED/utils/functions.py index 2fbc8a07..14cbb0d0 100644 --- a/src/geouned/GEOUNED/utils/functions.py +++ b/src/geouned/GEOUNED/utils/functions.py @@ -87,7 +87,7 @@ def __init__(self, id, comsolid=None): self.EnclosureID = None self.ParentEnclosureID = None self.SonEnclosures = [] - self.EnclosureList = None + self.enclosure_list = None self.CADSolid = None self.UniverseBox = None self.NullCell = True diff --git a/src/geouned/GEOUNED/void/void_functions.py b/src/geouned/GEOUNED/void/void_functions.py index ea552033..727e06e5 100644 --- a/src/geouned/GEOUNED/void/void_functions.py +++ b/src/geouned/GEOUNED/void/void_functions.py @@ -27,14 +27,14 @@ def assignEnclosure(MetaList, NestedEnclosure): # stop loop if CAD solid outside all level i enclosure or intersecting one if not outside: - m.EnclosureList = dep + m.enclosure_list = dep break if not dep: dep.add(-1) m.ParentEnclosureID = -1 if outside: - m.EnclosureList = dep + m.enclosure_list = dep return @@ -54,7 +54,7 @@ def select_solids(MetaList, LowLevelEnclosure, Enclosure): for m in MetaList: if m.IsEnclosure: continue - if enclID in m.EnclosureList: + if enclID in m.enclosure_list: newMetaList.append(m) return newMetaList From 46b602507cc976d644961810397a6856c39bc6eb Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 21 May 2024 15:58:25 +0100 Subject: [PATCH 06/27] moving load_cad and export_solids from start to own method (#186) * moved two methods out of start * removed unnecessary logger line --- src/geouned/GEOUNED/core.py | 100 ++++++++++++++-------- src/geouned/GEOUNED/utils/data_classes.py | 7 +- 2 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index abf0f3eb..a18f0967 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -25,6 +25,8 @@ from .write.write_files import write_geometry logger = logging.getLogger("general_logger") +logger.info(f"GEOUNED version {version('geouned')}") +logger.info(f"FreeCAD version {'.'.join(FreeCAD.Version()[:3])}") class CadToCsg: @@ -65,6 +67,8 @@ def __init__( self.numeric_format = numeric_format self.settings = settings + self.meta_list = [] + @property def stepFile(self): return self._stepFile @@ -434,29 +438,37 @@ def set(self, kwrd, value): else: self.__dict__["geometryName"] == value[:-4] - def start(self): + def _load_step_file( + self, + step_file: str, + # TODO consider having discrete indexes (1,5,7) instead of range (1,7) as this offers more flexibility to the user + cell_range: typing.Union[type(None), typing.Tuple[int, int]] = None, + ): + """ + Load STEP file(s) and extract solid volumes and enclosure volumes. - logger.info("start") - freecad_version = ".".join(FreeCAD.Version()[:3]) - logger.info(f"GEOUNED version {version('geouned')} \nFreeCAD version {freecad_version}") + Args: + step_file (str): The path to the STEP file or a list of paths to multiple STEP files. + cell_range (tuple[int, int], optional): A tuple representing the range of solids to select from the original STEP solids. Defaults to None. + + Returns: + tuple: A tuple containing the solid volumes list and enclosure volumes list extracted from the STEP files. + """ - if self.stepFile == "": - raise ValueError("Cannot run the code. Step file name is missing") + logger.info("Start of step file loading phase") - if isinstance(self.stepFile, (tuple, list)): - for stp in self.stepFile: + if isinstance(step_file, (tuple, list)): + for stp in step_file: if not path.isfile(stp): raise FileNotFoundError(f"Step file {stp} not found.\nStop.") else: - if not path.isfile(self.stepFile): - raise FileNotFoundError(f"Step file {self.stepFile} not found.\nStop.") + if not path.isfile(step_file): + raise FileNotFoundError(f"Step file {step_file} not found.\nStop.") - startTime = datetime.now() - - if isinstance(self.stepFile, (list, tuple)): - step_files = self.stepFile + if isinstance(step_file, (list, tuple)): + step_files = step_file else: - step_files = [self.stepFile] + step_files = [step_file] MetaChunk = [] EnclosureChunk = [] for stp in tqdm(step_files, desc="Loading CAD files"): @@ -464,30 +476,50 @@ def start(self): Meta, Enclosure = Load.load_cad(stp, self.settings, self.options) MetaChunk.append(Meta) EnclosureChunk.append(Enclosure) - self.meta_list: typing.List[UF.GeounedSolid] = join_meta_lists(MetaChunk) - self.enclosure_list: typing.List[UF.GeounedSolid] = join_meta_lists(EnclosureChunk) + self.meta_list = join_meta_lists(MetaChunk) + self.enclosure_list = join_meta_lists(EnclosureChunk) + + # Select a specific solid range from original STEP solids + if cell_range: + self.meta_list = self.meta_list[cell_range[0] : cell_range[1]] + + logger.info("End of step file loading phase") + + return self.meta_list, self.enclosure_list + + def _export_solids(self, filename: str): + """Export all the solid volumes from the loaded geometry to a STEP file. + + Args: + filename (str): filepath of the output STEP file. + """ + # export in STEP format solids read from input file + if self.meta_list == []: + raise ValueError( + "No solids in CadToCsg.meta_list to export. Try loading the STEP file first with CadToCsg._load_step_file" + ) + solids = [] + for m in self.meta_list: + if m.IsEnclosure: + continue + solids.extend(m.Solids) + Part.makeCompound(solids).exportStep(filename) + + def start(self): + + startTime = datetime.now() + + # sets the self.meta_list and self.enclosure_list + self._load_step_file(step_file=self.stepFile, cell_range=self.settings.cellRange) + + if self.settings.exportSolids: + self._export_solids(filename=self.settings.exportSolids) logger.info("End of loading phase") tempstr1 = str(datetime.now() - startTime) logger.info(tempstr1) tempTime = datetime.now() - # Select a specific solid range from original STEP solids - if self.settings.cellRange: - self.meta_list = self.meta_list[self.settings.cellRange[0] : self.settings.cellRange[1]] - - # export in STEP format solids read from input file - # terminate excution - if self.settings.exportSolids != "": - solids = [] - for m in self.meta_list: - if m.IsEnclosure: - continue - solids.extend(m.Solids) - Part.makeCompound(solids).exportStep(self.settings.exportSolids) - msg = f"Solids exported in file {self.settings.exportSolids}\n" "GEOUNED Finish. No solid translation performed." - raise ValueError(msg) - # set up Universe if self.enclosure_list: self.UniverseBox = get_universe(self.meta_list + self.enclosure_list) @@ -851,7 +883,7 @@ def print_warning_solids(warnSolids, warnEnclosures): solids_logger.info(lines) -def join_meta_lists(MList): +def join_meta_lists(MList) -> typing.List[UF.GeounedSolid]: newMetaList = MList[0] if MList[0]: diff --git a/src/geouned/GEOUNED/utils/data_classes.py b/src/geouned/GEOUNED/utils/data_classes.py index b29de9a2..ceabfda0 100644 --- a/src/geouned/GEOUNED/utils/data_classes.py +++ b/src/geouned/GEOUNED/utils/data_classes.py @@ -1,3 +1,4 @@ +import typing from numbers import Real @@ -648,7 +649,7 @@ def __init__( compSolids: bool = True, simplify: str = "no", cellRange: list = [], - exportSolids: str = "", + exportSolids: typing.Optional[str] = None, minVoidSize: float = 200.0, # units mm maxSurf: int = 50, maxBracket: int = 30, @@ -744,7 +745,9 @@ def exportSolids(self): @exportSolids.setter def exportSolids(self, exportSolids: str): - if not isinstance(exportSolids, str): + if exportSolids == None: + pass + elif not isinstance(exportSolids, str): raise TypeError(f"geouned.Tolerances.exportSolids should be a str, not a {type(exportSolids)}") self._exportSolids = exportSolids From 7207755b3db6c70f3437f8e33dd5a559dcb0c3f2 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 22 May 2024 07:57:21 +0100 Subject: [PATCH 07/27] Moving get universe out of start method (#188) * moved two methods out of start * removed unnecessary logger line * calculating bb if still None * improved comment * private method for now --- src/geouned/GEOUNED/core.py | 94 ++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index a18f0967..32f06b7d 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -67,6 +67,8 @@ def __init__( self.numeric_format = numeric_format self.settings = settings + # define later when running the code + self.geometry_bounding_box = None self.meta_list = [] @property @@ -184,8 +186,12 @@ def export_csg( if not isinstance(arg, str): raise TypeError(f"{arg} should be of type str not {type(arg_str)}") + # if the geometry_bounding_box has not previuosly been calculated, then make a default one + if self.geometry_bounding_box is None: + self._get_geometry_bounding_box() + write_geometry( - UniverseBox=self.UniverseBox, + UniverseBox=self.geometry_bounding_box, MetaList=self.meta_list, Surfaces=self.Surfaces, settings=self.settings, @@ -442,7 +448,7 @@ def _load_step_file( self, step_file: str, # TODO consider having discrete indexes (1,5,7) instead of range (1,7) as this offers more flexibility to the user - cell_range: typing.Union[type(None), typing.Tuple[int, int]] = None, + cell_range: typing.Union[None, typing.Tuple[int, int]] = None, ): """ Load STEP file(s) and extract solid volumes and enclosure volumes. @@ -505,6 +511,44 @@ def _export_solids(self, filename: str): solids.extend(m.Solids) Part.makeCompound(solids).exportStep(filename) + def _get_geometry_bounding_box(self, padding: float = 10.0): + """ + Get the bounding box of the geometry. + + Args: + padding (float): The padding value to add to the bounding box dimensions. + + Returns: + FreeCAD.BoundBox: The universe bounding box. + """ + # set up Universe + meta_list = self.meta_list + if self.enclosure_list: + meta_list += self.enclosure_list + + Box = meta_list[0].BoundBox + xmin = Box.XMin + xmax = Box.XMax + ymin = Box.YMin + ymax = Box.YMax + zmin = Box.ZMin + zmax = Box.ZMax + for m in meta_list[1:]: + # MIO. This was removed since in HELIAS the enclosure cell is the biggest one + # if m.IsEnclosure: continue + xmin = min(m.BoundBox.XMin, xmin) + xmax = max(m.BoundBox.XMax, xmax) + ymin = min(m.BoundBox.YMin, ymin) + ymax = max(m.BoundBox.YMax, ymax) + zmin = min(m.BoundBox.ZMin, zmin) + zmax = max(m.BoundBox.ZMax, zmax) + + self.geometry_bounding_box = FreeCAD.BoundBox( + FreeCAD.Vector(xmin - padding, ymin - padding, zmin - padding), + FreeCAD.Vector(xmax + padding, ymax + padding, zmax + padding), + ) + return self.geometry_bounding_box + def start(self): startTime = datetime.now() @@ -520,11 +564,8 @@ def start(self): logger.info(tempstr1) tempTime = datetime.now() - # set up Universe - if self.enclosure_list: - self.UniverseBox = get_universe(self.meta_list + self.enclosure_list) - else: - self.UniverseBox = get_universe(self.meta_list) + # sets self.geometry_bounding_box with default padding + self._get_geometry_bounding_box() self.Surfaces = UF.SurfacesDict(offset=self.settings.startSurf - 1) @@ -552,7 +593,7 @@ def start(self): cones = Conv.cellDef( m, self.Surfaces, - self.UniverseBox, + self.geometry_bounding_box, self.options, self.tolerances, self.numeric_format, @@ -572,7 +613,7 @@ def start(self): translate( self.meta_list, self.Surfaces, - self.UniverseBox, + self.geometry_bounding_box, self.settings, self.options, self.tolerances, @@ -592,7 +633,7 @@ def start(self): cones = Conv.cellDef( m, self.Surfaces, - self.UniverseBox, + self.geometry_bounding_box, self.options, self.tolerances, self.numeric_format, @@ -622,7 +663,7 @@ def start(self): meta_reduced, self.enclosure_list, self.Surfaces, - self.UniverseBox, + self.geometry_bounding_box, self.settings, init, self.options, @@ -705,7 +746,7 @@ def start(self): self.meta_list, coneInfo, self.Surfaces, - self.UniverseBox, + self.geometry_bounding_box, self.options, self.tolerances, self.numeric_format, @@ -743,7 +784,7 @@ def _decompose_solids(self, meta: bool): comsolid, err = Decom.SplitSolid( Part.makeCompound(m.Solids), - self.UniverseBox, + self.geometry_bounding_box, self.options, self.tolerances, self.numeric_format, @@ -770,7 +811,7 @@ def _decompose_solids(self, meta: bool): Decom.extract_surfaces( comsolid, "All", - self.UniverseBox, + self.geometry_bounding_box, self.options, self.tolerances, self.numeric_format, @@ -829,31 +870,6 @@ def process_cones(MetaList, coneInfo, Surfaces, UniverseBox, options, tolerances ) -def get_universe(MetaList): - d = 10 - Box = MetaList[0].BoundBox - xmin = Box.XMin - xmax = Box.XMax - ymin = Box.YMin - ymax = Box.YMax - zmin = Box.ZMin - zmax = Box.ZMax - for m in MetaList[1:]: - # MIO. This was removed since in HELIAS the enclosure cell is the biggest one - # if m.IsEnclosure: continue - xmin = min(m.BoundBox.XMin, xmin) - xmax = max(m.BoundBox.XMax, xmax) - ymin = min(m.BoundBox.YMin, ymin) - ymax = max(m.BoundBox.YMax, ymax) - zmin = min(m.BoundBox.ZMin, zmin) - zmax = max(m.BoundBox.ZMax, zmax) - - return FreeCAD.BoundBox( - FreeCAD.Vector(xmin - d, ymin - d, zmin - d), - FreeCAD.Vector(xmax + d, ymax + d, zmax + d), - ) - - def print_warning_solids(warnSolids, warnEnclosures): solids_logger = logging.getLogger("solids_logger") From cafe1a787469daab5a20d2549edd301b63153479 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 22 May 2024 07:59:35 +0100 Subject: [PATCH 08/27] Remove need for os import (#185) * removed os.path and os.mkdir * format * defined folder * passing str to exportStep * format * corrected argmument name --- src/geouned/GEOUNED/core.py | 54 +++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index 32f06b7d..996cfc42 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -3,7 +3,6 @@ import logging import typing from datetime import datetime -from os import mkdir, path from pathlib import Path from typing import get_type_hints from importlib.metadata import version @@ -446,7 +445,7 @@ def set(self, kwrd, value): def _load_step_file( self, - step_file: str, + filename: str, # TODO consider having discrete indexes (1,5,7) instead of range (1,7) as this offers more flexibility to the user cell_range: typing.Union[None, typing.Tuple[int, int]] = None, ): @@ -454,7 +453,7 @@ def _load_step_file( Load STEP file(s) and extract solid volumes and enclosure volumes. Args: - step_file (str): The path to the STEP file or a list of paths to multiple STEP files. + filename (str): The path to the STEP file or a list of paths to multiple STEP files. cell_range (tuple[int, int], optional): A tuple representing the range of solids to select from the original STEP solids. Defaults to None. Returns: @@ -463,23 +462,20 @@ def _load_step_file( logger.info("Start of step file loading phase") - if isinstance(step_file, (tuple, list)): - for stp in step_file: - if not path.isfile(stp): - raise FileNotFoundError(f"Step file {stp} not found.\nStop.") + if isinstance(filename, (list, tuple)): + step_files = filename else: - if not path.isfile(step_file): - raise FileNotFoundError(f"Step file {step_file} not found.\nStop.") + step_files = [filename] + + for step_file in step_files: + if not Path(step_file).is_file(): + raise FileNotFoundError(f"Step file {step_file} not found.") - if isinstance(step_file, (list, tuple)): - step_files = step_file - else: - step_files = [step_file] MetaChunk = [] EnclosureChunk = [] - for stp in tqdm(step_files, desc="Loading CAD files"): - logger.info(f"read step file : {stp}") - Meta, Enclosure = Load.load_cad(stp, self.settings, self.options) + for step_file in tqdm(step_files, desc="Loading CAD files"): + logger.info(f"read step file : {step_file}") + Meta, Enclosure = Load.load_cad(step_file, self.settings, self.options) MetaChunk.append(Meta) EnclosureChunk.append(Enclosure) self.meta_list = join_meta_lists(MetaChunk) @@ -554,7 +550,7 @@ def start(self): startTime = datetime.now() # sets the self.meta_list and self.enclosure_list - self._load_step_file(step_file=self.stepFile, cell_range=self.settings.cellRange) + self._load_step_file(filename=self.stepFile, cell_range=self.settings.cellRange) if self.settings.exportSolids: self._export_solids(filename=self.settings.exportSolids) @@ -774,13 +770,13 @@ def _decompose_solids(self, meta: bool): continue logger.info(f"Decomposing solid: {i + 1}/{totsolid}") if self.settings.debug: + debug_output_folder = Path("debug") logger.info(m.Comments) - if not path.exists("debug"): - mkdir("debug") + debug_output_folder.mkdir(parents=True, exist_ok=True) if m.IsEnclosure: - m.Solids[0].exportStep(f"debug/origEnclosure_{i}.stp") + m.Solids[0].exportStep(str(debug_output_folder / f"origEnclosure_{i}.stp")) else: - m.Solids[0].exportStep(f"debug/origSolid_{i}.stp") + m.Solids[0].exportStep(str(debug_output_folder / f"origSolid_{i}.stp")) comsolid, err = Decom.SplitSolid( Part.makeCompound(m.Solids), @@ -791,22 +787,22 @@ def _decompose_solids(self, meta: bool): ) if err != 0: - if not path.exists("Suspicious_solids"): - mkdir("Suspicious_solids") + sus_output_folder = Path("suspicious_solids") + sus_output_folder.mkdir(parents=True, exist_ok=True) if m.IsEnclosure: - Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Enclosure_original_{i}.stp") - comsolid.exportStep(f"Suspicious_solids/Enclosure_split_{i}.stp") + Part.CompSolid(m.Solids).exportStep(str(sus_output_folder / f"Enclosure_original_{i}.stp")) + comsolid.exportStep(str(sus_output_folder / f"Enclosure_split_{i}.stp")) else: - Part.CompSolid(m.Solids).exportStep(f"Suspicious_solids/Solid_original_{i}.stp") - comsolid.exportStep(f"Suspicious_solids/Solid_split_{i}.stp") + Part.CompSolid(m.Solids).exportStep(str(sus_output_folder / f"Solid_original_{i}.stp")) + comsolid.exportStep(str(sus_output_folder / f"Solid_split_{i}.stp")) warningSolids.append(i) if self.settings.debug: if m.IsEnclosure: - comsolid.exportStep(f"debug/compEnclosure_{i}.stp") + comsolid.exportStep(str(debug_output_folder / f"compEnclosure_{i}.stp")) else: - comsolid.exportStep(f"debug/compSolid_{i}.stp") + comsolid.exportStep(str(debug_output_folder / f"compSolid_{i}.stp")) self.Surfaces.extend( Decom.extract_surfaces( comsolid, From 793d4f36cf025b668844f342ac051618d3130e6a Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 22 May 2024 11:24:07 +0100 Subject: [PATCH 09/27] corrected badge urls (#190) Co-authored-by: AlvaroCubi <55387701+AlvaroCubi@users.noreply.github.com> --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 72397ef8..ed33239e 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [![documentation](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml) [![PyPI](https://img.shields.io/pypi/v/geouned?&label=PyPI)](https://pypi.org/project/geouned/) -[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/version.svg)](https://anaconda.org/fusion-energy/geouned) -[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/platforms.svg)](https://anaconda.org/fusion-energy/geouned) -[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/downloads.svg)](https://anaconda.org/fusion-energy/geouned) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/version.svg)](https://anaconda.org/conda-forge/geouned) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/platforms.svg)](https://anaconda.org/conda-forge/geouned) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/downloads.svg)](https://anaconda.org/conda-forge/geouned) # GEOUNED A tool to convert from CAD to CSG & CSG to CAD for Monte Carlo transport codes (MCNP & OpenMC). From d7806c08a330a3a84147490786706a0d5e04a3d1 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 23 May 2024 17:18:54 +0100 Subject: [PATCH 10/27] Adding docs for json api usage (#192) * moved two methods out of start * removed unnecessary logger line * added from json example --- docs/usage/python_api_usage.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_api_usage.rst index 0b59ca62..d83c78e1 100644 --- a/docs/usage/python_api_usage.rst +++ b/docs/usage/python_api_usage.rst @@ -1,8 +1,7 @@ Python Package Usage ==================== -The Python API has two main classes. -The first main class is ``CadToCsg()`` which converts CAD geometry to Constructive Solid Geometry (CSG). +The main class is ``CadToCsg()`` which converts CAD geometry to Constructive Solid Geometry (CSG). There are many arguments that can be passed into the ``CadToCsg()`` class which are documented in the `Python API reference section <../python_api.html>`_ of the documentation. @@ -117,3 +116,15 @@ The following example shows a usage with every attributes specified. cellCommentFile=False, cellSummaryFile=False, ) + +You can also load up JSON configuration files from the API. +The JSON file format is also usable with the Geouned Command Line Interface and there are more `complete examples of JSON files `_ on that section of the documentation. +For this example we assume you have a JSON file called 'config.json' in the same directory as the script. + +.. code-block:: python + + import geouned + + geo = geouned.CadToCsg.from_json("config.json") + geo.start() + geo.export_csg() From 0e52196cf5c2587ee679307bdac8a2ff4752a085 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 23 May 2024 17:20:38 +0100 Subject: [PATCH 11/27] Adding UCARD to missing parts of documentation and example JSON file (#194) * moved two methods out of start * removed unnecessary logger line * added UCARD null to missing parts * full step files list * added check for ucard above 1 * Apply suggestions from code review --- docs/usage/python_cli_usage.rst | 3 ++- src/geouned/GEOUNED/core.py | 5 ++++- tests/config_complete_defaults.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/usage/python_cli_usage.rst b/docs/usage/python_cli_usage.rst index 3db4d750..51f15c87 100644 --- a/docs/usage/python_cli_usage.rst +++ b/docs/usage/python_cli_usage.rst @@ -102,12 +102,13 @@ Here is a complete JSON file specification "outFormat": ["openmc_xml", "openmc_py", "serpent", "phits", "mcnp"], "volSDEF": false, "volCARD": true, + "UCARD": null, "dummyMat": false, "cellCommentFile": false, "cellSummaryFile": true } } - +Note that JSON requires ```null``` to be passed in which gets translated to ```None``` in Python. This is converted in the same way as the minimal JSON config file .. code-block:: bash diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index 996cfc42..a8a4faf5 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -135,7 +135,7 @@ def export_csg( ), volSDEF: bool = False, volCARD: bool = True, - UCARD: typing.Union[int, type(None)] = 101, + UCARD: typing.Union[int, None] = None, dummyMat: bool = False, cellCommentFile: bool = False, cellSummaryFile: bool = True, @@ -170,6 +170,9 @@ def export_csg( if not isinstance(UCARD, int) and not isinstance(UCARD, type(None)): raise TypeError(f"UCARD should be of type int or None not {type(UCARD)}") + if isinstance(UCARD, int): + if UCARD < 0: + raise ValueError("UCARD should be a 0 or a positive integer ") for arg, arg_str in ( (volSDEF, "volSDEF"), diff --git a/tests/config_complete_defaults.json b/tests/config_complete_defaults.json index ef931ae8..da5256c7 100644 --- a/tests/config_complete_defaults.json +++ b/tests/config_complete_defaults.json @@ -69,6 +69,7 @@ "outFormat": ["openmc_xml", "openmc_py", "serpent", "phits", "mcnp"], "volSDEF": false, "volCARD": true, + "UCARD": null, "dummyMat": false, "cellCommentFile": false, "cellSummaryFile": true From 721ed45f3ba30861fb37d866cb77e2f870fd91e9 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Mon, 27 May 2024 09:29:44 +0100 Subject: [PATCH 12/27] Adding version support to docs (#198) * testing new theme for versioning * moved version to navbar start * keeping gh-pages files * docs * removed if push * publish dev sub dir * added dev version * pointing to dev repo * added more versions * removed comment * updated versions * using version match * added version to title * general tidy * added github link * corrected url * format * Typo fix --- .github/workflows/documentation_release.yml | 51 +++++++++++++++++++ ...ation.yml => documentation_update_dev.yml} | 12 +++-- docs/conf.py | 37 +++++++++++--- docs/index.rst | 6 ++- docs/install/install_windows.rst | 4 +- docs/version_switcher.json | 12 +++++ pyproject.toml | 3 +- 7 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/documentation_release.yml rename .github/workflows/{documentation.yml => documentation_update_dev.yml} (76%) create mode 100644 docs/version_switcher.json diff --git a/.github/workflows/documentation_release.yml b/.github/workflows/documentation_release.yml new file mode 100644 index 00000000..d154a523 --- /dev/null +++ b/.github/workflows/documentation_release.yml @@ -0,0 +1,51 @@ +name: documentation release + +on: + push: + tags: + - '*' + +permissions: + contents: write + +jobs: + testing: + name: Documentation + runs-on: "ubuntu-latest" + defaults: + run: + shell: bash -el {0} + strategy: + matrix: + python-version: ["3.11"] + steps: + - name: checkout actions + uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v3 + with: + auto-update-conda: true + python-version: ${{ matrix.python-version }} + channels: conda-forge + + - name: install dependencies + run: conda install -c conda-forge freecad -y + + - name: install package + run: | + pip install --upgrade pip + pip install .[docs] + - name: Sphinx build + run: | + sphinx-build docs _build/${{ github.ref_name }} + - name: Deploy to GitHub Pages + if: github.event_name == 'push' + uses: peaceiris/actions-gh-pages@v4 + with: + publish_branch: gh-pages + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: _build/ + # with next rlease of actions-gh-pages + # issue to allow force_orphan will be fixed + # https://github.com/peaceiris/actions-gh-pages/issues/455 + # force_orphan: true + keep_files: true diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation_update_dev.yml similarity index 76% rename from .github/workflows/documentation.yml rename to .github/workflows/documentation_update_dev.yml index 41934563..d5d8a3ac 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation_update_dev.yml @@ -1,4 +1,4 @@ -name: documentation +name: documentation update dev on: pull_request: @@ -41,12 +41,16 @@ jobs: pip install .[docs] - name: Sphinx build run: | - sphinx-build docs _build + sphinx-build docs _build/dev - name: Deploy to GitHub Pages if: github.event_name == 'push' uses: peaceiris/actions-gh-pages@v4 with: publish_branch: gh-pages github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: _build/ - force_orphan: true + publish_dir: _build + # with next rlease of actions-gh-pages + # issue to allow force_orphan will be fixed + # https://github.com/peaceiris/actions-gh-pages/issues/455 + # force_orphan: true + keep_files: true \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 4af84bb2..acc1b9cd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,6 +11,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # import os +import re import sys sys.path.insert(0, os.path.abspath("../../src")) @@ -22,7 +23,10 @@ author = "Juan-Pablo Catalan and Patrick Sauvan" # The full version, including alpha/beta/rc tags -release = "1.0.1" +import geouned + +version = geouned.__version__ +release = geouned.__version__ # -- General configuration --------------------------------------------------- @@ -51,17 +55,34 @@ # -- Options for HTML output ------------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" +# The theme to use for HTML and HTML Help pages. +# this theme supports versions https://github.com/pydata/pydata-sphinx-theme +html_theme = "pydata_sphinx_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -html_context = { - "display_github": True, -} +# TODO add logo # html_favicon = "favicon.ico" + +# Version match must match the 'version' key in version_switcher.json +pattern = re.compile(r"^[0-9]+\.[0-9]+") +version_match = pattern.search(version) +if version_match: + version_match = version_match.group() +elif "dev" in version: + version_match = "dev" +else: + version_match = version + +html_theme_options = { + "github_url": "https://github.com/GEOUNED-org/GEOUNED", + "switcher": { + "json_url": "https://raw.githubusercontent.com/fusion-neutronics/GEOUNED/adding_version_support_to_docs/docs/version_switcher.json", + "version_match": version_match, + }, + "nav_title": "Geouned", + "navbar_start": ["version-switcher", "navbar-icon-links"], +} diff --git a/docs/index.rst b/docs/index.rst index b0c162b4..b63c7c50 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,8 +3,10 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -GEOUNED -======= +Geouned Documentation +===================== + +**Version**: |version| GEOUNED converts CAD to Constructive Solid Geometry (CSG) formats for use in Monte Carlo transport codes. diff --git a/docs/install/install_windows.rst b/docs/install/install_windows.rst index b9214dc1..ac339217 100644 --- a/docs/install/install_windows.rst +++ b/docs/install/install_windows.rst @@ -11,7 +11,7 @@ You can follow the install instructions for `Miniforge3 here `_ or from the link below +You can get it from the `Miniforge3 GitHub repository here `_ or from the link below `https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe `_ @@ -58,7 +58,7 @@ You can follow the install instructions for `MiniConda3 `_ or from the link below +You can get it from the `Miniforge3 GitHub repository here `_ or from the link below `https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe `_ diff --git a/docs/version_switcher.json b/docs/version_switcher.json new file mode 100644 index 00000000..3f86e201 --- /dev/null +++ b/docs/version_switcher.json @@ -0,0 +1,12 @@ +[ + { + "name": "dev", + "version": "dev", + "url": "https://geouned-org.github.io/GEOUNED/dev" + }, + { + "name": "1.1.0", + "version": "1.1.0", + "url": "https://geouned-org.github.io/GEOUNED/1.1.0" + } +] \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ea4bbfd9..f8fcb9f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,9 +43,8 @@ tests = [ ] docs = [ "sphinx", - "sphinx_rtd_theme", + "pydata-sphinx-theme", "sphinx_autodoc_typehints", - "sphinx_rtd_theme", ] [project.scripts] From 2f76c1efc1241219f036f662e114ee2327b93bb0 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Mon, 27 May 2024 12:54:45 +0100 Subject: [PATCH 13/27] added missing " (#200) --- docs/usage/python_api_usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_api_usage.rst index d83c78e1..aed77297 100644 --- a/docs/usage/python_api_usage.rst +++ b/docs/usage/python_api_usage.rst @@ -90,7 +90,7 @@ The following example shows a usage with every attributes specified. ) geo = geouned.CadToCsg( - stepFile="cuboid.stp, + stepFile="cuboid.stp", options=my_options, settings=my_settings, tolerances=my_tolerances, From ef80ed22fec7826c533081733091eddddc57df60 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 28 May 2024 08:35:41 +0100 Subject: [PATCH 14/27] Adding more details to the developer documentation (#202) * moved two methods out of start * removed unnecessary logger line * added dev docs * removed random commit file --- docs/developer_guide.rst | 230 ++++++++++++++++++++++++++++++++- docs/install/install_linux.rst | 93 ------------- 2 files changed, 229 insertions(+), 94 deletions(-) diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index 555da6f9..f3fb10dc 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -1,4 +1,232 @@ Developer guide =============== -TOOD \ No newline at end of file +Developer install with Mamba +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. + +You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. +Download + +.. code-block:: sh + + curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" + bash Miniforge3-$(uname)-$(uname -m).sh + +Install Miniforge3 + +.. code-block:: sh + + bash Miniforge3-Linux-x86_64.sh + + +Activate the base environment in your current terminal + +.. code-block:: sh + + mamba activate + + +It is recommended to create a new environment + +.. code-block:: sh + + mamba create --name new_env python=3.11 + + +Activate the new environment + +.. code-block:: sh + + mamba activate new_env + +We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. +Install FreeCAD which is the main dependency + +.. code-block:: sh + + mamba install -c conda-forge freecad + +Fork the GEOUNED-org/GEOUNED repository by clicking this link, unchecking the Copy the main branch only check box and clicking create fork + +`https://github.com/GEOUNED-org/GEOUNED/fork `_ + +Assuming that you have `setup `_ and `added `_ SSH keys then we can clone your forked GEOUNED repository. +Replace with your own github username + +.. code-block:: sh + + git clone git@github.com:/GEOUNED.git + +Then change directory into the repository root like this + +.. code-block:: sh + + cd GEOUNED + +Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. +We are also adding the -e to get an editable install so that when you make local changes to the repo these are picked up in your Python scripts straight away (without needing to reinstall). +We also include all the optional dependencies so that we can run tests locally and build the docs locally. + +.. code-block:: sh + + python -m pip install -e .[tests,docs] + +Then you will be able to run import GEOUNED from within Python + +.. code-block:: python + + import geouned + +You will also be able to use the GEOUNED command line tool + +.. code-block:: bash + + geouned_cadtocsg --help + +Checkout feature branches from dev and make local changes on you own branch + +.. code-block:: sh + + git checkout dev + git checkout -b 'my_new_feature' + +Pull requests are welcome + +Building the docs locally +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: sh + + python -m pip install -e .[docs] + sphinx-build docs _build + +Then view the docs by opening the _build/index.html file in a web browser. + +When the CI builds docs it puts the latest stable version in the _build directory on the gh-pages branch. + +Versions (including dev) are built and put in subdirectories of the _build directory on the gh-pages branch. + +Running the tests locally +~~~~~~~~~~~~~~~~~~~~~~~~~ + +As we installed the tests dependencies using the [tests] option the we can run the tests locally with pytest. + + +.. code-block:: sh + + python -m pip install -e .[tests,docs] + +However we need one more dependency to run the tests. + +.. code-block:: sh + + mamba install -c conda-forge openmc + +Then we can run the tests with the following command from the root of the repository. + +.. code-block:: sh + python -m pytest + +We can run individual test files by specifying the file path + +.. code-block:: sh + python -m pytest tests/test_convert.py + +We can run individual test functions by specifying the file path and function name + +.. code-block:: sh + python -m pytest tests/test_convert.py -k 'test_conversion' + +Additional pytest options that might be useful are including -s for standard output and -vv for very verbose output. + +.. code-block:: sh + + python -m pytest -s -vv + +Merging a pull requests +~~~~~~~~~~~~~~~~~~~~~~~ + +Pull requests should be made from feature branches on a fork of the repository to the dev branch. + +Tests checking the code will run automatically on the pull request. + +If the tests pass and at least one approver approves then the pull request can be merged. + +When a pull request is ready to be merged then the pull request should be **squashed** and merged into the dev branch. + +Releasing a new version +~~~~~~~~~~~~~~~~~~~~~~~ + +To release a new version we first need to add and entry to the docs/version_switcher.json file on the dev branch + +.. code-block:: python + + [ + { + "name": "dev", + "version": "dev", + "url": "https://geouned-org.github.io/GEOUNED/dev" + }, + { + "name": "1.1.0", + "version": "1.1.0", + "url": "https://geouned-org.github.io/GEOUNED/1.1.0" + } + ] + +For example adding version 1.2.3 would look like this + +.. code-block:: python + + [ + { + "name": "dev", + "version": "dev", + "url": "https://geouned-org.github.io/GEOUNED/dev" + }, + { + "name": "1.1.0", + "version": "1.1.0", + "url": "https://geouned-org.github.io/GEOUNED/1.1.0" + }, + { + "name": "1.2.3", + "version": "1.2.3", + "url": "https://geouned-org.github.io/GEOUNED/1.2.3" + } + ] + +Then create a `pull request from dev branch to main branch `_ + +Once the tests for this pass then merge the pull request in. **Do not squash** this pull request as we want to keep the history of the version changes. + +Then `create a new release on the main branch `_ with the version number and a description of the changes. + +Create a new tag with the version number (e.g. 1.2.3) and the release name (e.g. v1.2.3) and the release description. + +Press the Generate release notes button to get the release notes from the pull request descriptions. + +Then press the Publish release button to create the release. + +This will create the release and trigger github actions for +- publishing the PyPI package +- building the docs and setting the default docs to the new version + +Check the actions both pass by going to the `actions tab https://github.com/shimwell/GEOUNED/actions>`_ on the repository and checking the latest actions. + +Conda Forge Releasing +~~~~~~~~~~~~~~~~~~~~~ + +The conda-forge package release is done after the PyPI release. This is because the conda-forge package is built from the PyPI package. + +Conda Forge has a bot thew generates a pull request to update the conda-forge recipe. This is done automatically when the PyPI package is released. + +The pull request will be generated in the `conda-forge/GEOUNED-feedstock `_ repository. + +Check the pull request and if the tests pass then merge the pull request. + +A Conda Forge package will be built and released to the conda-forge channel. + +Once released the package will be visaible on the `conda-forge channel `_. diff --git a/docs/install/install_linux.rst b/docs/install/install_linux.rst index 30822452..1353c82b 100644 --- a/docs/install/install_linux.rst +++ b/docs/install/install_linux.rst @@ -118,99 +118,6 @@ You will also be able to use the GEOUNED command line tool geouned_cadtocsg --help -Developer install with Mamba -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. - -You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. -Download - -.. code-block:: sh - - curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - bash Miniforge3-$(uname)-$(uname -m).sh - -Install Miniforge3 - -.. code-block:: sh - - bash Miniforge3-Linux-x86_64.sh - - -Activate the base environment in your current terminal - -.. code-block:: sh - - mamba activate - - -It is recommended to create a new environment - -.. code-block:: sh - - mamba create --name new_env python=3.11 - - -Activate the new environment - -.. code-block:: sh - - mamba activate new_env - -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency - -.. code-block:: sh - - mamba install -c conda-forge freecad - -Fork the GEOUNED-org/GEOUNED repository by clicking this link, unchecking the Copy the main branch only check box and clicking create fork - -`https://github.com/GEOUNED-org/GEOUNED/fork `_ - -Assuming that you have `setup `_ and `added `_ SSH keys then we can clone your forked GEOUNED repository. -Replace with your own github username - -.. code-block:: sh - - git clone git@github.com:/GEOUNED.git - -Then change directory into the repository root like this - -.. code-block:: sh - - cd GEOUNED - -Install GEOUNED with pip, we also prefix this with "python -m" to ensure that pip install uses the correct Python interpreter. -We are also adding the -e to get an editable install so that when you make local changes to the repo these are picked up in your Python scripts straight away (without needing to reinstall). -We also include all the optional dependencies so that we can run tests locally and build the docs locally. - -.. code-block:: sh - - python -m pip install -e .[tests,docs] - -Then you will be able to run import GEOUNED from within Python - -.. code-block:: python - - import geouned - -You will also be able to use the GEOUNED command line tool - -.. code-block:: bash - - geouned_cadtocsg --help - -Checkout feature branches from dev and make local changes on you own branch - -.. code-block:: sh - - git checkout dev - git checkout -b 'my_new_feature' - -Pull requests are welcome - .. Apt-get .. ~~~~~~~ From 1762a3aadbeb799bd1c36f2a98929a33694d2154 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 4 Jun 2024 19:09:54 +0100 Subject: [PATCH 15/27] Changing cellRange to discrete numbers to allow more flexability when loading geometry (#205) * moved two methods out of start * removed unnecessary logger line * changed cellRange to skip_solids * improved doc string * lowercase args pep8 --- docs/usage/python_api_usage.rst | 10 ++- docs/usage/python_cli_usage.rst | 10 ++- src/geouned/GEOUNED/core.py | 78 +++++++++++---------- src/geouned/GEOUNED/utils/data_classes.py | 52 +++++--------- src/geouned/GEOUNED/write/mcnp_format.py | 10 +-- src/geouned/GEOUNED/write/phits_format.py | 18 ++--- src/geouned/GEOUNED/write/serpent_format.py | 10 +-- src/geouned/GEOUNED/write/write_files.py | 8 +-- tests/config_complete_defaults.json | 6 +- tests/config_minimal.json | 4 +- tests/config_non_defaults.json | 4 +- tests/test_convert.py | 30 +++----- tests/test_volumes.py | 25 +++++++ 13 files changed, 139 insertions(+), 126 deletions(-) diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_api_usage.rst index aed77297..43462eaf 100644 --- a/docs/usage/python_api_usage.rst +++ b/docs/usage/python_api_usage.rst @@ -12,7 +12,8 @@ The example makes use of default attributes. .. code-block:: python import geouned - geo = geouned.CadToCsg(stepFile='cuboid.stp') + geo = geouned.CadToCsg() + geo.load_step_file(filename='cuboid.stp') geo.start() geo.export_csg() @@ -43,7 +44,6 @@ The following example shows a usage with every attributes specified. debug=False, compSolids=True, simplify="no", - cellRange=[], exportSolids="", minVoidSize=200.0, maxSurf=50, @@ -90,13 +90,17 @@ The following example shows a usage with every attributes specified. ) geo = geouned.CadToCsg( - stepFile="cuboid.stp", options=my_options, settings=my_settings, tolerances=my_tolerances, numeric_format=my_numeric_format, ) + geo.load_step_file( + filename="cuboid.stp", + skip_solids=[], + ) + geo.start() geo.export_csg( diff --git a/docs/usage/python_cli_usage.rst b/docs/usage/python_cli_usage.rst index 51f15c87..1cf983f0 100644 --- a/docs/usage/python_cli_usage.rst +++ b/docs/usage/python_cli_usage.rst @@ -12,7 +12,9 @@ First create a JSON file called "config.json" containing the following. .. code-block:: json { - "stepFile": "cuboid.stp" + "load_step_file": { + "filename":"cuboid.stp" + } } Then execute the command line interface tool to convert your STEP file to CSG files with the default configuration. @@ -32,7 +34,10 @@ Here is a complete JSON file specification .. code-block:: json { - "stepFile": "cuboid.stp", + "load_step_file": { + "filename": "cuboid.stp", + "skip_solids": [] + }, "Options": { "forceCylinder": false, "newSplitPlane": true, @@ -85,7 +90,6 @@ Here is a complete JSON file specification "debug": false, "compSolids": true, "simplify": "no", - "cellRange": [], "exportSolids": "", "minVoidSize": 200.0, "maxSurf": 50, diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index a8a4faf5..5522ec8e 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -32,8 +32,6 @@ class CadToCsg: """Base class for the conversion of CAD to CSG models Args: - stepFile (str, optional): Name of the CAD file (in STEP format) to - be converted. Defaults to "". options (geouned.Options, optional): An instance of a geouned.Options class with the attributes set for the desired conversion. Defaults to a geouned.Options with default attributes values. @@ -53,14 +51,12 @@ class with the attributes set for the desired conversion. Defaults def __init__( self, - stepFile: str = "", options: Options = Options(), tolerances: Tolerances = Tolerances(), numeric_format: NumericFormat = NumericFormat(), settings: Settings = Settings(), ): - self.stepFile = stepFile self.options = options self.tolerances = tolerances self.numeric_format = numeric_format @@ -69,16 +65,8 @@ def __init__( # define later when running the code self.geometry_bounding_box = None self.meta_list = [] - - @property - def stepFile(self): - return self._stepFile - - @stepFile.setter - def stepFile(self, value: str): - if not isinstance(value, str): - raise TypeError(f"geouned.CadToCsg.stepFile should be a str, not a {type(value)}") - self._stepFile = value + self.filename = None + self.skip_solids = [] @property def options(self): @@ -209,20 +197,23 @@ def export_csg( volCARD=volCARD, UCARD=UCARD, dummyMat=dummyMat, - stepFile=self.stepFile, + step_filename=self.filename, ) logger.info("End of Monte Carlo code translation phase") @classmethod def from_json(cls, filename: str): - """Creates a CadToCsg instance and returns the instance. Populating the - Options, Tolerance, Settings and NumericFormat attributes from matching - key names in the JSON. If export_to_csg key is present then this method - also runs .start() and .export_to_csg() on the instance. + """Creates a CadToCsg instance, runs CadToCsg.load_Step_file(), runs + CadToCsg.start() and returns the instance. Populating the arguments for + the methods that are run by looking for keys with the same name as the + method in the JSON file. For example CadToCsg.start() accepts arguments + for Options, Tolerance, Settings and NumericFormat and can be populated + from matching key names in the JSON. If export_to_csg key is present + then this method also runs CadToCsg.export_to_csg() on the instance. Args: - filename str: The filename of the config file. Defaults to "config.json". + filename str: The filename of the config file. Raises: FileNotFoundError: If the config file is not found @@ -238,10 +229,13 @@ def from_json(cls, filename: str): with open(filename) as f: config = json.load(f) - cad_to_csg = cls(stepFile=config["stepFile"]) + cad_to_csg = cls() + + cad_to_csg.load_step_file(**config["load_step_file"]) + for key in config.keys(): - if key in ["stepFile", "export_csg"]: + if key in ["load_step_file", "export_csg"]: pass # these two keys are used before or after this loop elif key == "Tolerances": @@ -258,7 +252,7 @@ def from_json(cls, filename: str): else: raise ValueError( - f"Invalid key '{key}' found in config file {filename}. Acceptable key names are 'stepFile', 'export_csg', 'Settings', 'Parameters', 'Tolerances' and 'NumericFormat'" + f"Invalid key '{key}' found in config file {filename}. Acceptable key names are 'load_step_file', 'export_csg', 'Settings', 'Parameters', 'Tolerances' and 'NumericFormat'" ) cad_to_csg.start() @@ -446,25 +440,39 @@ def set(self, kwrd, value): else: self.__dict__["geometryName"] == value[:-4] - def _load_step_file( + def load_step_file( self, - filename: str, - # TODO consider having discrete indexes (1,5,7) instead of range (1,7) as this offers more flexibility to the user - cell_range: typing.Union[None, typing.Tuple[int, int]] = None, + filename: typing.Union[str, typing.Sequence[str]], + skip_solids: typing.Sequence[int] = [], ): """ Load STEP file(s) and extract solid volumes and enclosure volumes. Args: filename (str): The path to the STEP file or a list of paths to multiple STEP files. - cell_range (tuple[int, int], optional): A tuple representing the range of solids to select from the original STEP solids. Defaults to None. + skip_solids (Sequence[int], optional): A sequence (list or tuple) of indexes of solids to not load for conversion. Returns: tuple: A tuple containing the solid volumes list and enclosure volumes list extracted from the STEP files. """ - logger.info("Start of step file loading phase") + if not isinstance(skip_solids, (list, tuple)): + raise TypeError(f"skip_solids should be a list, tuple of ints, not a {type(skip_solids)}") + for entry in skip_solids: + if not isinstance(entry, int): + raise TypeError(f"skip_solids should contain only ints, not a {type(entry)}") + + if not isinstance(filename, (str, list, tuple)): + raise TypeError(f"filename should be a str or a sequence of str, not a {type(filename)}") + if isinstance(filename, (list, tuple)): + for entry in filename: + if not isinstance(entry, str): + raise TypeError(f"filename should contain only str, not a {type(entry)}") + + self.filename = filename + self.skip_solids = skip_solids + if isinstance(filename, (list, tuple)): step_files = filename else: @@ -484,9 +492,10 @@ def _load_step_file( self.meta_list = join_meta_lists(MetaChunk) self.enclosure_list = join_meta_lists(EnclosureChunk) - # Select a specific solid range from original STEP solids - if cell_range: - self.meta_list = self.meta_list[cell_range[0] : cell_range[1]] + # deleting the solid index in reverse order so the indexes don't change for subsequent deletions + for solid_index in sorted(skip_solids, reverse=True): + logger.info(f"Removing solid index: {solid_index} from list of {len(self.meta_list)} solids") + del self.meta_list[solid_index] logger.info("End of step file loading phase") @@ -501,7 +510,7 @@ def _export_solids(self, filename: str): # export in STEP format solids read from input file if self.meta_list == []: raise ValueError( - "No solids in CadToCsg.meta_list to export. Try loading the STEP file first with CadToCsg._load_step_file" + "No solids in CadToCsg.meta_list to export. Try loading the STEP file first with CadToCsg.load_step_file" ) solids = [] for m in self.meta_list: @@ -552,9 +561,6 @@ def start(self): startTime = datetime.now() - # sets the self.meta_list and self.enclosure_list - self._load_step_file(filename=self.stepFile, cell_range=self.settings.cellRange) - if self.settings.exportSolids: self._export_solids(filename=self.settings.exportSolids) diff --git a/src/geouned/GEOUNED/utils/data_classes.py b/src/geouned/GEOUNED/utils/data_classes.py index ceabfda0..32ac49ae 100644 --- a/src/geouned/GEOUNED/utils/data_classes.py +++ b/src/geouned/GEOUNED/utils/data_classes.py @@ -594,8 +594,6 @@ class Settings: """Settings for changing the way the CAD to CSG conversion is done Args: - stepFile (str, optional): Name of the CAD file (in STEP format) to - be converted. Defaults to "". matFile (str, optional): _description_. Defaults to "". voidGen (bool, optional): Generate voids of the geometry. Defaults to True. @@ -616,9 +614,6 @@ class Settings: 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". - cellRange (list, optional): Range of cell to be converted (only one - range is allowed, e.g [100,220]). Default all solids are - converted. Defaults to []. exportSolids (str, optional): Export CAD solid after reading. The execution is stopped after export, the translation is not carried out. Defaults to "". @@ -648,7 +643,6 @@ def __init__( debug: bool = False, compSolids: bool = True, simplify: str = "no", - cellRange: list = [], exportSolids: typing.Optional[str] = None, minVoidSize: float = 200.0, # units mm maxSurf: int = 50, @@ -665,7 +659,6 @@ def __init__( self.debug = debug self.compSolids = compSolids self.simplify = simplify - self.cellRange = cellRange self.exportSolids = exportSolids self.minVoidSize = minVoidSize self.maxSurf = maxSurf @@ -683,7 +676,7 @@ def matFile(self): @matFile.setter def matFile(self, matFile: str): if not isinstance(matFile, str): - raise TypeError(f"geouned.Tolerances.matFile should be a str, not a {type(matFile)}") + raise TypeError(f"geouned.Settings.matFile should be a str, not a {type(matFile)}") self._matFile = matFile @property @@ -693,7 +686,7 @@ def voidGen(self): @voidGen.setter def voidGen(self, voidGen: bool): if not isinstance(voidGen, bool): - raise TypeError(f"geouned.Tolerances.voidGen should be a bool, not a {type(voidGen)}") + raise TypeError(f"geouned.Settings.voidGen should be a bool, not a {type(voidGen)}") self._voidGen = voidGen @property @@ -703,7 +696,7 @@ def debug(self): @debug.setter def debug(self, debug: bool): if not isinstance(debug, bool): - raise TypeError(f"geouned.Tolerances.debug should be a bool, not a {type(debug)}") + raise TypeError(f"geouned.Settings.debug should be a bool, not a {type(debug)}") self._debug = debug @property @@ -713,7 +706,7 @@ def compSolids(self): @compSolids.setter def compSolids(self, compSolids: bool): if not isinstance(compSolids, bool): - raise TypeError(f"geouned.Tolerances.compSolids should be a bool, not a {type(compSolids)}") + raise TypeError(f"geouned.Settings.compSolids should be a bool, not a {type(compSolids)}") self._compSolids = compSolids @property @@ -723,22 +716,9 @@ def simplify(self): @simplify.setter def simplify(self, simplify: str): if not isinstance(simplify, str): - raise TypeError(f"geouned.Tolerances.simplify should be a str, not a {type(simplify)}") + raise TypeError(f"geouned.Settings.simplify should be a str, not a {type(simplify)}") self._simplify = simplify - @property - def cellRange(self): - return self._cellRange - - @cellRange.setter - def cellRange(self, cellRange: list): - if not isinstance(cellRange, list): - raise TypeError(f"geouned.Tolerances.cellRange should be a list, not a {type(cellRange)}") - for entry in cellRange: - if not isinstance(entry, int): - raise TypeError(f"geouned.Tolerances.cellRange should be a list of ints, not a {type(entry)}") - self._cellRange = cellRange - @property def exportSolids(self): return self._exportSolids @@ -748,7 +728,7 @@ def exportSolids(self, exportSolids: str): if exportSolids == None: pass elif not isinstance(exportSolids, str): - raise TypeError(f"geouned.Tolerances.exportSolids should be a str, not a {type(exportSolids)}") + raise TypeError(f"geouned.Settings.exportSolids should be a str, not a {type(exportSolids)}") self._exportSolids = exportSolids @property @@ -758,7 +738,7 @@ def minVoidSize(self): @minVoidSize.setter def minVoidSize(self, minVoidSize: float): if not isinstance(minVoidSize, float): - raise TypeError(f"geouned.Tolerances.minVoidSize should be a float, not a {type(minVoidSize)}") + raise TypeError(f"geouned.Settings.minVoidSize should be a float, not a {type(minVoidSize)}") self._minVoidSize = minVoidSize @property @@ -768,7 +748,7 @@ def maxSurf(self): @maxSurf.setter def maxSurf(self, maxSurf: int): if not isinstance(maxSurf, int): - raise TypeError(f"geouned.Tolerances.maxSurf should be a int, not a {type(maxSurf)}") + raise TypeError(f"geouned.Settings.maxSurf should be a int, not a {type(maxSurf)}") self._maxSurf = maxSurf @property @@ -778,7 +758,7 @@ def maxBracket(self): @maxBracket.setter def maxBracket(self, maxBracket: int): if not isinstance(maxBracket, int): - raise TypeError(f"geouned.Tolerances.maxBracket should be a int, not a {type(maxBracket)}") + raise TypeError(f"geouned.Settings.maxBracket should be a int, not a {type(maxBracket)}") self._maxBracket = maxBracket @property @@ -788,10 +768,10 @@ def voidMat(self): @voidMat.setter def voidMat(self, voidMat: list): if not isinstance(voidMat, list): - raise TypeError(f"geouned.Tolerances.voidMat should be a list, not a {type(voidMat)}") + raise TypeError(f"geouned.Settings.voidMat should be a list, not a {type(voidMat)}") for entry in voidMat: if not isinstance(entry, int): - raise TypeError(f"geouned.Tolerances.voidMat should be a list of ints, not a {type(entry)}") + raise TypeError(f"geouned.Settings.voidMat should be a list of ints, not a {type(entry)}") self._voidMat = voidMat @property @@ -801,10 +781,10 @@ def voidExclude(self): @voidExclude.setter def voidExclude(self, voidExclude: list): if not isinstance(voidExclude, list): - raise TypeError(f"geouned.Tolerances.voidExclude should be a list, not a {type(voidExclude)}") + 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.Tolerances.voidExclude should be a list of ints, not a {type(entry)}") + raise TypeError(f"geouned.Settings.voidExclude should be a list of ints, not a {type(entry)}") self._voidExclude = voidExclude @property @@ -814,7 +794,7 @@ def startCell(self): @startCell.setter def startCell(self, startCell: int): if not isinstance(startCell, int): - raise TypeError(f"geouned.Tolerances.startCell should be a int, not a {type(startCell)}") + raise TypeError(f"geouned.Settings.startCell should be a int, not a {type(startCell)}") self._startCell = startCell @property @@ -824,7 +804,7 @@ def startSurf(self): @startSurf.setter def startSurf(self, startSurf: int): if not isinstance(startSurf, int): - raise TypeError(f"geouned.Tolerances.startSurf should be a int, not a {type(startSurf)}") + raise TypeError(f"geouned.Settings.startSurf should be a int, not a {type(startSurf)}") self._startSurf = startSurf @property @@ -834,5 +814,5 @@ def sort_enclosure(self): @sort_enclosure.setter def sort_enclosure(self, sort_enclosure: bool): if not isinstance(sort_enclosure, bool): - raise TypeError(f"geouned.Tolerances.sort_enclosure should be a bool, not a {type(sort_enclosure)}") + raise TypeError(f"geouned.Settings.sort_enclosure should be a bool, not a {type(sort_enclosure)}") self._sort_enclosure = sort_enclosure diff --git a/src/geouned/GEOUNED/write/mcnp_format.py b/src/geouned/GEOUNED/write/mcnp_format.py index 5d8ef81c..e5690262 100644 --- a/src/geouned/GEOUNED/write/mcnp_format.py +++ b/src/geouned/GEOUNED/write/mcnp_format.py @@ -30,7 +30,7 @@ def __init__( volCARD, UCARD, dummyMat, - stepFile, + step_filename, ): self.Title = title self.VolSDEF = volSDEF @@ -48,9 +48,9 @@ def __init__( } self.part = "P" - self.StepFile = stepFile - if isinstance(self.StepFile, (tuple, list)): - self.StepFile = "; ".join(self.StepFile) + self.step_filename = step_filename + if isinstance(self.step_filename, (tuple, list)): + self.step_filename = "; ".join(self.step_filename) self.get_surface_table() self.simplify_planes(Surfaces) @@ -119,7 +119,7 @@ def write_header(self): Information = f"""C C ************************************************************* -C Original Step file : {self.StepFile} +C Original Step file : {self.step_filename} C C Creation Date : {datetime.now()} C Solid Cells : {self.__solidCells__} diff --git a/src/geouned/GEOUNED/write/phits_format.py b/src/geouned/GEOUNED/write/phits_format.py index 08a25ee9..043c2855 100644 --- a/src/geouned/GEOUNED/write/phits_format.py +++ b/src/geouned/GEOUNED/write/phits_format.py @@ -44,7 +44,7 @@ def __init__( volCARD, UCARD, dummyMat, - stepFile, + step_filename, matFile, voidMat, startCell, @@ -63,12 +63,12 @@ def __init__( self.options = options self.Options = {"Volume": self.VolCARD, "Universe": self.U0CARD} - self.StepFile = stepFile - if isinstance(self.StepFile, (tuple, list)): - self.StepFile = "; ".join(self.StepFile) + self.step_filename = step_filename + if isinstance(self.step_filename, (tuple, list)): + self.step_filename = "; ".join(self.step_filename) if self.Title == "": - self.Title = self.StepFile + self.Title = self.step_filename self.get_surface_table() self.simplify_planes(Surfaces) @@ -160,7 +160,7 @@ def write_phits_header(self): Information = f"""$ $ ************************************************************* -$ Original Step file : {self.StepFile} +$ Original Step file : {self.step_filename} $ $ Creation Date : {datetime.now()} $ Solid Cells : {self.__solidCells__} @@ -180,7 +180,7 @@ def write_phits_header(self): def write_phits_cell_block(self): enclenvChk = [] - enclenvChk = self.stepfile_label_chk(self.StepFile) + enclenvChk = self.step_filename_label_chk(self.step_filename) if enclenvChk: logger.info("Unified the inner void cell(s) definition") @@ -192,7 +192,7 @@ def write_phits_cell_block(self): self.write_phits_cells(cell) return - def stepfile_label_chk(self, filename): + def step_filename_label_chk(self, filename): enclenvList = [] with open(filename) as f: @@ -410,7 +410,7 @@ def write_phits_volume_block(self): eliminated_endVoidIndex = self.__cells__ + self.startCell - 3 enclenvChk = [] - enclenvChk = self.stepfile_label_chk(self.StepFile) + enclenvChk = self.step_filename_label_chk(self.step_filename) if enclenvChk and self.Options["Volume"]: for i, cell in enumerate(self.Cells): diff --git a/src/geouned/GEOUNED/write/serpent_format.py b/src/geouned/GEOUNED/write/serpent_format.py index f58f5741..7164bc92 100644 --- a/src/geouned/GEOUNED/write/serpent_format.py +++ b/src/geouned/GEOUNED/write/serpent_format.py @@ -28,7 +28,7 @@ def __init__( volCARD, UCARD, dummyMat, - stepFile, + step_filename, ): self.options = options self.tolerances = tolerances @@ -46,9 +46,9 @@ def __init__( } self.part = "p" - self.StepFile = stepFile - if isinstance(self.StepFile, (tuple, list)): - self.StepFile = "; ".join(self.StepFile) + self.step_filename = step_filename + if isinstance(self.step_filename, (tuple, list)): + self.step_filename = "; ".join(self.step_filename) self.get_surface_table() self.simplify_planes(Surfaces) @@ -113,7 +113,7 @@ def write_header(self): Information = f"""% % ************************************************************* -% Original Step file : {self.StepFile} +% Original Step file : {self.step_filename} % % Creation Date : {datetime.now()} % Solid Cells : {self.__solidCells__} diff --git a/src/geouned/GEOUNED/write/write_files.py b/src/geouned/GEOUNED/write/write_files.py index 995efcd9..1b9a12d3 100644 --- a/src/geouned/GEOUNED/write/write_files.py +++ b/src/geouned/GEOUNED/write/write_files.py @@ -22,7 +22,7 @@ def write_geometry( volCARD, UCARD, dummyMat, - stepFile, + step_filename, ): supported_mc_codes = ("mcnp", "openmc_xml", "openmc_py", "serpent", "phits") @@ -63,7 +63,7 @@ def write_geometry( volCARD, UCARD, dummyMat, - stepFile, + step_filename, ) MCNPfile.set_sdef((outSphere, outBox)) MCNPfile.write_input(mcnpFilename) @@ -105,7 +105,7 @@ def write_geometry( volCARD, UCARD, dummyMat, - stepFile, + step_filename, ) # Serpentfile.set_sdef((outSphere,outBox)) Serpentfile.write_input(serpentFilename) @@ -139,7 +139,7 @@ def write_geometry( volCARD, UCARD, dummyMat, - stepFile, + step_filename, matFile=settings.matFile, voidMat=settings.voidMat, startCell=settings.startCell, diff --git a/tests/config_complete_defaults.json b/tests/config_complete_defaults.json index da5256c7..496fe5c7 100644 --- a/tests/config_complete_defaults.json +++ b/tests/config_complete_defaults.json @@ -1,5 +1,8 @@ { - "stepFile": "testing/inputSTEP/BC.stp", + "load_step_file": { + "filename": "testing/inputSTEP/BC.stp", + "skip_solids": [] + }, "Options": { "forceCylinder": false, "newSplitPlane": true, @@ -52,7 +55,6 @@ "debug": false, "compSolids": true, "simplify": "no", - "cellRange": [], "exportSolids": "", "minVoidSize": 200.0, "maxSurf": 50, diff --git a/tests/config_minimal.json b/tests/config_minimal.json index 04761d79..bdacc345 100644 --- a/tests/config_minimal.json +++ b/tests/config_minimal.json @@ -1,3 +1,5 @@ { - "stepFile": "testing/inputSTEP/BC.stp" + "load_step_file": { + "filename":"testing/inputSTEP/BC.stp" + } } \ No newline at end of file diff --git a/tests/config_non_defaults.json b/tests/config_non_defaults.json index b8ff5cec..9c7deb05 100644 --- a/tests/config_non_defaults.json +++ b/tests/config_non_defaults.json @@ -1,5 +1,7 @@ { - "stepFile": "testing/inputSTEP/BC.stp", + "load_step_file": { + "filename":"testing/inputSTEP/BC.stp" + }, "Options": { "forceCylinder": true }, diff --git a/tests/test_convert.py b/tests/test_convert.py index 2998c565..a84be0ad 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -83,7 +83,6 @@ def test_conversion(input_step_file): debug=False, compSolids=True, simplify="no", - cellRange=[], exportSolids="", minVoidSize=200.0, # units mm maxSurf=50, @@ -96,13 +95,14 @@ def test_conversion(input_step_file): ) geo = geouned.CadToCsg( - stepFile=f"{input_step_file.resolve()}", options=my_options, settings=my_settings, tolerances=my_tolerances, numeric_format=my_numeric_format, ) + geo.load_step_file(filename=f"{input_step_file.resolve()}", skip_solids=[]) + geo.start() geo.export_csg( @@ -140,7 +140,7 @@ def test_cad_to_csg_from_json_with_defaults(input_json_file): my_cad_to_csg = geouned.CadToCsg.from_json(input_json_file) assert isinstance(my_cad_to_csg, geouned.CadToCsg) - assert my_cad_to_csg.stepFile == "testing/inputSTEP/BC.stp" + assert my_cad_to_csg.filename == "testing/inputSTEP/BC.stp" assert my_cad_to_csg.options.forceCylinder == False assert my_cad_to_csg.tolerances.relativeTol == False assert my_cad_to_csg.numeric_format.P_abc == "14.7e" @@ -149,13 +149,6 @@ def test_cad_to_csg_from_json_with_defaults(input_json_file): for suffix in suffixes: assert Path("csg").with_suffix(suffix).exists() - # deletes the output MC files if they already exists - for suffix in suffixes: - Path("csg").with_suffix(suffix).unlink(missing_ok=True) - - my_cad_to_csg.start() - my_cad_to_csg.export_csg() - def test_cad_to_csg_from_json_with_non_defaults(): @@ -166,7 +159,7 @@ def test_cad_to_csg_from_json_with_non_defaults(): my_cad_to_csg = geouned.CadToCsg.from_json("tests/config_non_defaults.json") assert isinstance(my_cad_to_csg, geouned.CadToCsg) - assert my_cad_to_csg.stepFile == "testing/inputSTEP/BC.stp" + assert my_cad_to_csg.filename == "testing/inputSTEP/BC.stp" assert my_cad_to_csg.options.forceCylinder == True assert my_cad_to_csg.tolerances.relativePrecision == 2e-6 assert my_cad_to_csg.numeric_format.P_abc == "15.7e" @@ -175,18 +168,12 @@ def test_cad_to_csg_from_json_with_non_defaults(): for suffix in suffixes: assert Path("csg").with_suffix(suffix).exists() - # deletes the output MC files if they already exists - for suffix in suffixes: - Path("csg").with_suffix(suffix).unlink(missing_ok=True) - - my_cad_to_csg.start() - my_cad_to_csg.export_csg() - def test_writing_to_new_folders(): """Checks that a folder is created prior to writing output files""" - geo = geouned.CadToCsg(stepFile="testing/inputSTEP/BC.stp") + geo = geouned.CadToCsg() + geo.load_step_file(filename="testing/inputSTEP/BC.stp", skip_solids=[]) geo.start() for outformat in ["mcnp", "phits", "serpent", "openmc_xml", "openmc_py"]: @@ -216,12 +203,13 @@ def test_with_relative_tol_true(): # more details https://github.com/GEOUNED-org/GEOUNED/issues/154 geo = geouned.CadToCsg( - stepFile=f"{step_files[1].resolve()}", tolerances=geouned.Tolerances(relativeTol=False), ) + geo.load_step_file(filename=f"{step_files[1].resolve()}", skip_solids=[]) geo.start() + geo = geouned.CadToCsg( - stepFile=f"{step_files[1].resolve()}", tolerances=geouned.Tolerances(relativeTol=True), ) + geo.load_step_file(filename=f"{step_files[1].resolve()}", skip_solids=[]) geo.start() diff --git a/tests/test_volumes.py b/tests/test_volumes.py index 94a65483..8678992f 100644 --- a/tests/test_volumes.py +++ b/tests/test_volumes.py @@ -13,6 +13,7 @@ import freecad # importing conda package if present except: pass +import geouned import openmc import Part import pytest @@ -124,3 +125,27 @@ def test_volumes(input_step_file): # converts from mm3 in cad to cm3 in csg volume_of_cad_cell = solid.Volume * 0.001 assert math.isclose(volume_of_cad_cell, volume_of_csg_cell, rel_tol=rel_tol) + + +@pytest.mark.parametrize("skip_solids,expected", [([], 12), ([1, 4, 6], 9)]) +def test_skip_solids_when_loading(skip_solids, expected): + """test to check that the correct number of cells are found in the CSG file when skip solids is set""" + + geo = geouned.CadToCsg(settings=geouned.Settings(voidGen=False, compSolids=False)) + + # this geometry has 12 solids in it + geo.load_step_file(filename="testing/inputSTEP/Torus/example.stp", skip_solids=skip_solids) + geo.start() + assert geo.filename == "testing/inputSTEP/Torus/example.stp" + assert geo.skip_solids == skip_solids + + geo.export_csg( + geometryName="tests_outputs/skip_solids/csg", + outFormat=["openmc_xml"], + ) + + materials = openmc.Materials() + geometry = openmc.Geometry.from_xml("tests_outputs/skip_solids/csg.xml", materials) + cells = geometry.get_all_cells() + all_cell_ids = cells.keys() + assert len(all_cell_ids) == expected From 58c245ee25cc9c7f2f2b59db3ae419a9237af7aa Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 4 Jun 2024 19:20:44 +0100 Subject: [PATCH 16/27] Adding y to install (#207) * moved two methods out of start * removed unnecessary logger line * changes while working therough the install --- docs/developer_guide.rst | 13 +++++++++---- docs/install/install_linux.rst | 4 ++-- docs/install/install_windows.rst | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index f3fb10dc..15d39607 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -6,6 +6,12 @@ Developer install with Mamba First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. +Conda could be used instead of Mamba but Mamba is faster and more reliable. If you don't have permission to install Miniforge3 then you could try using Conda instead. For you could install mamba into conda with. + +.. code-block:: sh + + conda install -c conda-forge mamba -y + You can follow the install instructions for `Miniforge3 here `_ or follows the commands below. Download @@ -41,12 +47,11 @@ Activate the new environment mamba activate new_env -We have aspirations to create a conda-forge package which will combine these final two steps, but for now FreeCAD and GEOUNED can be installed in two commands. -Install FreeCAD which is the main dependency +As our main dependency FreeCAD is not available on PYPi but we can install it from conda-forge. .. code-block:: sh - mamba install -c conda-forge freecad + mamba install -c conda-forge freecad -y Fork the GEOUNED-org/GEOUNED repository by clicking this link, unchecking the Copy the main branch only check box and clicking create fork @@ -122,7 +127,7 @@ However we need one more dependency to run the tests. .. code-block:: sh - mamba install -c conda-forge openmc + mamba install -c conda-forge openmc -y Then we can run the tests with the following command from the root of the repository. diff --git a/docs/install/install_linux.rst b/docs/install/install_linux.rst index 1353c82b..c7b6f62b 100644 --- a/docs/install/install_linux.rst +++ b/docs/install/install_linux.rst @@ -45,7 +45,7 @@ Install GEOUNED from conda-forge .. code-block:: sh - mamba install -c conda-forge geouned + mamba install -c conda-forge geouned -y Then you will be able to run import GEOUNED from within Python @@ -103,7 +103,7 @@ Install GEOUNED from conda-forge .. code-block:: sh - conda install -c conda-forge geouned + conda install -c conda-forge geouned -y Then you will be able to run import GEOUNED from within Python diff --git a/docs/install/install_windows.rst b/docs/install/install_windows.rst index ac339217..fae852d7 100644 --- a/docs/install/install_windows.rst +++ b/docs/install/install_windows.rst @@ -35,7 +35,7 @@ Install GEOUNED from conda-forge .. code-block:: sh - mamba install -c conda-forge geouned + mamba install -c conda-forge geouned -y Then you will be able to run import GEOUNED from within Python @@ -80,7 +80,7 @@ Install GEOUNED from conda-forge .. code-block:: sh - conda install -c conda-forge geouned + conda install -c conda-forge geouned -y Then you will be able to run import GEOUNED from within Python From 599fe67b21c1068d023be2714267285332ad662f Mon Sep 17 00:00:00 2001 From: Kyle Grammer <34066958+KBGrammer@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:53:26 -0400 Subject: [PATCH 17/27] Fix for #203, errors in simplify full (#206) * Patch for #203 * Responding to comments on #206 * Update core.py Add missing space to call to UF.get_box() --------- Co-authored-by: AlvaroCubi <55387701+AlvaroCubi@users.noreply.github.com> Co-authored-by: Grammer, Kyle --- .../GEOReverse/Modules/Utils/BooleanSolids.py | 6 +++--- src/geouned/GEOReverse/Modules/splitFunction.py | 6 +++--- src/geouned/GEOUNED/conversion/cell_definition.py | 4 ++-- src/geouned/GEOUNED/core.py | 4 ++-- src/geouned/GEOUNED/utils/boolean_solids.py | 14 +++++++------- src/geouned/GEOUNED/void/void_box_class.py | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py b/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py index 45125712..f86af187 100644 --- a/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py +++ b/src/geouned/GEOReverse/Modules/Utils/BooleanSolids.py @@ -434,7 +434,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = cut_box(box) + boxes, centers = divide_box(box) n = 0 while True: @@ -446,7 +446,7 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = cut_box(b) + btab, ctab = divide_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox @@ -458,7 +458,7 @@ def point_inside(solid): # divide a box into 8 smaller boxes -def cut_box(Box): +def divide_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 diff --git a/src/geouned/GEOReverse/Modules/splitFunction.py b/src/geouned/GEOReverse/Modules/splitFunction.py index 99c4e8b3..dcfb44d3 100644 --- a/src/geouned/GEOReverse/Modules/splitFunction.py +++ b/src/geouned/GEOReverse/Modules/splitFunction.py @@ -163,7 +163,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = cut_box(box) + boxes, centers = divide_box(box) n = 0 while True: @@ -175,7 +175,7 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = cut_box(b) + btab, ctab = divide_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox @@ -188,7 +188,7 @@ def point_inside(solid): # divide a box into 8 smaller boxes -def cut_box(Box): +def divide_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 diff --git a/src/geouned/GEOUNED/conversion/cell_definition.py b/src/geouned/GEOUNED/conversion/cell_definition.py index f67403cc..e0ab524d 100644 --- a/src/geouned/GEOUNED/conversion/cell_definition.py +++ b/src/geouned/GEOUNED/conversion/cell_definition.py @@ -856,7 +856,7 @@ def no_overlapping_cell(metaList, surfaces, options): CT = build_c_table_from_solids( box, (tuple(t_def.get_surfaces_numbers()), Surfs), - option="diag", + "diag", options=options, ) @@ -866,7 +866,7 @@ def no_overlapping_cell(metaList, surfaces, options): CT = build_c_table_from_solids( box, (tuple(new_def.get_surfaces_numbers()), Surfs), - option="full", + "full", options=options, ) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index 5522ec8e..bf67bb55 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -687,8 +687,8 @@ def start(self): if c.Definition.level == 0 or c.IsEnclosure: continue logger.info(f"simplify cell {c.__id__}") - Box = UF.get_box(c) - CT = build_c_table_from_solids(Box, (c.Surfaces, Surfs), option="full") + Box = UF.get_box(c, self.options.enlargeBox) + CT = build_c_table_from_solids(Box, (c.Surfaces, Surfs), "full", options=self.options) c.Definition.simplify(CT) c.Definition.clean() if type(c.Definition.elements) is bool: diff --git a/src/geouned/GEOUNED/utils/boolean_solids.py b/src/geouned/GEOUNED/utils/boolean_solids.py index 82591f76..be69ea97 100644 --- a/src/geouned/GEOUNED/utils/boolean_solids.py +++ b/src/geouned/GEOUNED/utils/boolean_solids.py @@ -239,7 +239,7 @@ def combine_diag_elements(d1, d2): return CTelement((0, 0, 1, 0)) -def build_c_table_from_solids(Box, SurfInfo, option, options): +def build_c_table_from_solids(Box, SurfInfo, simplification_mode, options): if type(SurfInfo) is dict: surfaces = SurfInfo @@ -260,7 +260,7 @@ def build_c_table_from_solids(Box, SurfInfo, option, options): surfaces[s].buildShape(Box.BoundBox) CTable = ConstraintTable() - if option == "diag": + if simplification_mode == "diag": CTable.diagonal = True else: CTable.diagonal = False @@ -270,7 +270,7 @@ def build_c_table_from_solids(Box, SurfInfo, option, options): # res,splitRegions = split_solid_fast(Box,Surfaces.get_surface(s1),True) CTable.add_element(s1, s1, CTelement(res, s1, s1)) - if option == "diag": + if simplification_mode == "diag": continue if splitRegions is None: continue # loop, no region to be split by s2 @@ -309,7 +309,7 @@ def build_c_table_from_solids(Box, SurfInfo, option, options): CTable.add_element(s1, s2, CTelement(val, s1, s2)) # if some surfaces don't cross the box some elements in Constraint table are not filled - if option != "diag": + if simplification_mode != "diag": CTable.fill_missing_elements() return CTable @@ -460,7 +460,7 @@ def point_inside(solid): BBox = solid.optimalBoundingBox(False) box = [BBox.XMin, BBox.XMax, BBox.YMin, BBox.YMax, BBox.ZMin, BBox.ZMax] - boxes, centers = cut_box(box) + boxes, centers = divide_box(box) n = 0 while True: @@ -472,7 +472,7 @@ def point_inside(solid): subbox = [] centers = [] for b in boxes: - btab, ctab = cut_box(b) + btab, ctab = divide_box(b) subbox.extend(btab) centers.extend(ctab) boxes = subbox @@ -515,7 +515,7 @@ def point_from_surface(solid): # divide a box into 8 smaller boxes -def cut_box(Box): +def divide_box(Box): xmid = (Box[1] + Box[0]) * 0.5 ymid = (Box[3] + Box[2]) * 0.5 zmid = (Box[5] + Box[4]) * 0.5 diff --git a/src/geouned/GEOUNED/void/void_box_class.py b/src/geouned/GEOUNED/void/void_box_class.py index b3d3ae9f..bc9a55af 100644 --- a/src/geouned/GEOUNED/void/void_box_class.py +++ b/src/geouned/GEOUNED/void/void_box_class.py @@ -271,7 +271,7 @@ def get_void_complementary(self, Surfaces, options, tolerances, numeric_format, surfaceDict = {} for i in surfList: surfaceDict[i] = Surfaces.get_surface(i) - CTable = build_c_table_from_solids(Box, surfaceDict, option=simplify, options=options) + CTable = build_c_table_from_solids(Box, surfaceDict, simplify, options=options) else: if res is True: return None, None From a3fd78f2edc5fd765ccafa86d3b98cfe8cccc5c3 Mon Sep 17 00:00:00 2001 From: Kyle Grammer <34066958+KBGrammer@users.noreply.github.com> Date: Fri, 7 Jun 2024 04:03:32 -0400 Subject: [PATCH 18/27] Simplify error (#211) * Patch for #203 * Responding to comments on #206 * Update core.py Add missing space to call to UF.get_box() * Replace all get_box calls to have options.enlargeBox, change get_box to refer only to enlargeBox instead of options.enlargeBox --------- Co-authored-by: AlvaroCubi <55387701+AlvaroCubi@users.noreply.github.com> Co-authored-by: Grammer, Kyle --- src/geouned/GEOUNED/conversion/cell_definition.py | 2 +- src/geouned/GEOUNED/utils/functions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/geouned/GEOUNED/conversion/cell_definition.py b/src/geouned/GEOUNED/conversion/cell_definition.py index e0ab524d..91ed386e 100644 --- a/src/geouned/GEOUNED/conversion/cell_definition.py +++ b/src/geouned/GEOUNED/conversion/cell_definition.py @@ -849,7 +849,7 @@ def no_overlapping_cell(metaList, surfaces, options): t_def, simplify = t_def_and_simplify if True in simplify: logger.info(f"reduce cell {m.__id__}") - box = UF.get_box(m) + box = UF.get_box(m, options.enlargeBox) # evaluate only diagonal elements of the Constraint Table (fastest) and remove surface not # crossing in the solid boundBox diff --git a/src/geouned/GEOUNED/utils/functions.py b/src/geouned/GEOUNED/utils/functions.py index 14cbb0d0..b5d251b0 100644 --- a/src/geouned/GEOUNED/utils/functions.py +++ b/src/geouned/GEOUNED/utils/functions.py @@ -23,9 +23,9 @@ from . import basic_functions_part2 as BF -def get_box(comp, options): +def get_box(comp, enlargeBox): bb = FreeCAD.BoundBox(comp.BoundBox) - bb.enlarge(options.enlargeBox) + bb.enlarge(enlargeBox) xMin, yMin, zMin = bb.XMin, bb.YMin, bb.ZMin xLength, yLength, zLength = bb.XLength, bb.YLength, bb.ZLength From e093b780660643aac6dcbc94dfe72b796c47c4f3 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 13 Jun 2024 14:38:47 +0200 Subject: [PATCH 19/27] fixed versions url (#217) --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index acc1b9cd..8ab45740 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -80,7 +80,7 @@ html_theme_options = { "github_url": "https://github.com/GEOUNED-org/GEOUNED", "switcher": { - "json_url": "https://raw.githubusercontent.com/fusion-neutronics/GEOUNED/adding_version_support_to_docs/docs/version_switcher.json", + "json_url": "https://raw.githubusercontent.com/GEOUNED-org/GEOUNED/dev/docs/version_switcher.json", "version_match": version_match, }, "nav_title": "Geouned", From 072fde44cf8f4a56478406fcaa91c6ed1e226725 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 13 Jun 2024 17:08:58 +0200 Subject: [PATCH 20/27] removed config ini code (#218) * removed config ini code * format --- src/geouned/GEOUNED/core.py | 179 ------------------------------------ 1 file changed, 179 deletions(-) diff --git a/src/geouned/GEOUNED/core.py b/src/geouned/GEOUNED/core.py index bf67bb55..0125f4d5 100644 --- a/src/geouned/GEOUNED/core.py +++ b/src/geouned/GEOUNED/core.py @@ -1,4 +1,3 @@ -import configparser import json import logging import typing @@ -262,184 +261,6 @@ def from_json(cls, filename: str): cad_to_csg.export_csg() return cad_to_csg - # TODO add tests as set_configuration is not currently tested - def set_configuration(self, configFile=None): - - if configFile is None: - return - - config = configparser.ConfigParser() - config.optionxform = str - config.read(configFile) - for section in config.sections(): - if section == "Files": - for key in config["Files"].keys(): - if key in ("geometryName", "matFile", "title"): - self.set(key, config.get("Files", key)) - - elif key == "stepFile": - value = config.get("Files", key).strip() - lst = value.split() - if value[0] in ("(", "[") and value[-1] in ("]", ")"): - data = value[1:-1].split(",") - data = [x.strip() for x in data] - self.set(key, data) - elif len(lst) > 1: - self.set(key, lst) - else: - self.set(key, value) - - elif key == "outFormat": - raw = config.get("Files", key).strip() - values = tuple(x.strip() for x in raw.split(",")) - outFormat = [] - for v in values: - if v.lower() == "mcnp": - outFormat.append("mcnp") - elif v.lower() == "openmc_xml": - outFormat.append("openmc_xml") - elif v.lower() == "openmc_py": - outFormat.append("openmc_py") - elif v.lower() == "serpent": - outFormat.append("serpent") - elif v.lower() == "phits": - outFormat.append("phits") - self.set(key, tuple(outFormat)) - - elif section == "Parameters": - for key in config["Parameters"].keys(): - if key in ( - "voidGen", - "debug", - "compSolids", - "volSDEF", - "volCARD", - "dummyMat", - "cellSummaryFile", - "cellCommentFile", - "sort_enclosure", - ): - self.set(key, config.getboolean("Parameters", key)) - elif key in ( - "minVoidSize", - "maxSurf", - "maxBracket", - "startCell", - "startSurf", - ): - self.set(key, config.getint("Parameters", key)) - elif key in ("exportSolids", "UCARD", "simplify"): - self.set(key, config.get("Parameters", key)) - elif key == "voidMat": - value = config.get("Parameters", key).strip() - data = value[1:-1].split(",") - self.set(key, (int(data[0]), float(data[1]), data[2])) - else: - value = config.get("Parameters", key).strip() - data = value[1:-1].split(",") - self.set(key, tuple(map(int, data))) - - elif section == "Options": - attributes_and_types = get_type_hints(Options()) - for key in config["Options"].keys(): - if key in attributes_and_types.keys(): - if attributes_and_types[key] is bool: - value = config.getboolean("Options", key) - elif attributes_and_types[key] is float or attributes_and_types[key] is int: - value = config.getfloat("Options", key) - setattr(self.options, key, value) - - elif section == "Tolerances": - attributes_and_types = get_type_hints(Tolerances()) - for key in config["Tolerances"].keys(): - if key in attributes_and_types.keys(): - if attributes_and_types[key] is bool: - value = config.getboolean("Tolerances", key) - elif attributes_and_types[key] is float: - value = config.getfloat("Tolerances", key) - setattr(self.tolerances, key, value) - - elif section == "MCNP_Numeric_Format": - attributes_and_types = get_type_hints(NumericFormat()) - PdEntry = False - for key in config["MCNP_Numeric_Format"].keys(): - if key in attributes_and_types.keys(): - value = config.get("MCNP_Numeric_Format", key) - setattr(self.numeric_format, key, value) - if key == "P_d": - PdEntry = True - - else: - logger.info(f"bad section name : {section}") - - if self.__dict__["geometryName"] == "": - self.__dict__["geometryName"] = self.__dict__["stepFile"][:-4] - - # TODO see if we can find another way to do this - if self.options.prnt3PPlane and not PdEntry: - self.NumericFormat.P_d = "22.15e" - - logger.info(self.__dict__) - - # TODO add tests as set is not currently tested - def set(self, kwrd, value): - - if kwrd == "stepFile": - if isinstance(value, (list, tuple)): - for v in value: - if not isinstance(v, str): - logger.info(f"elemt in {kwrd} list should be string") - return - elif not isinstance(value, str): - logger.info(f"{kwrd} should be string or tuple of strings") - return - - elif kwrd == "UCARD": - if value == "None": - value = None - elif value.isdigit(): - value = int(value) - else: - logger.info(f"{kwrd} value should be None or integer") - return - elif kwrd == "outFormat": - if len(value) == 0: - return - elif kwrd in ("geometryName", "matFile", "exportSolids"): - if not isinstance(value, str): - logger.info(f"{kwrd} value should be str instance") - return - elif kwrd in ("cellRange", "voidMat", "voidExclude"): - if not isinstance(value, (list, tuple)): - logger.info(f"{kwrd} value should be list or tuple") - return - elif kwrd in ("minVoidSize", "maxSurf", "maxBracket", "startCell", "startSurf"): - if not isinstance(value, int): - logger.info(f"{kwrd} value should be integer") - return - elif kwrd in ( - "voidGen", - "debug", - "compSolids", - "simplifyCTable", - "volSDEF", - "volCARD", - "dummyMat", - "cellSummaryFile", - "cellCommentFile", - "sort_enclosure", - ): - if not isinstance(value, bool): - logger.info(f"{kwrd} value should be boolean") - return - - self.__dict__[kwrd] = value - if kwrd == "stepFile" and self.__dict__["geometryName"] == "": - if isinstance(value, (tuple, list)): - self.__dict__["geometryName"] == "joined_step_files" - else: - self.__dict__["geometryName"] == value[:-4] - def load_step_file( self, filename: typing.Union[str, typing.Sequence[str]], From 47d746325ba29938cc201ed3caf62fe3f56f5036 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 13 Jun 2024 17:33:00 +0200 Subject: [PATCH 21/27] added part from alex comment (#219) --- docs/developer_guide.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index 15d39607..8ecc11b7 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -2,7 +2,7 @@ Developer guide =============== Developer install with Mamba -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First we need to install a Mamba distribution. There are a few options but here we opt for Miniforge3 as it includes Mamba. @@ -235,3 +235,8 @@ Check the pull request and if the tests pass then merge the pull request. A Conda Forge package will be built and released to the conda-forge channel. Once released the package will be visaible on the `conda-forge channel `_. + +Version Numbering +~~~~~~~~~~~~~~~~~ + +GEOUNED will use Semantic Versioning to number releases of the tool, in the form "Major.Minor.Patch", e.g., “3.15.9”. From ec9a61b2dda6011b594228a9974843741643ccac Mon Sep 17 00:00:00 2001 From: Alberto <4104972+alberto743@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:39:02 +0200 Subject: [PATCH 22/27] Specify in the developer guide that openmc and pytest are needed to run tests (#221) --- docs/developer_guide.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index 8ecc11b7..eeea924e 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -53,6 +53,12 @@ As our main dependency FreeCAD is not available on PYPi but we can install it fr mamba install -c conda-forge freecad -y +To be able to run test cases and stochastic volume calculation, it is needed to install ``openmc`` and ``pytest`` from conda-forge. + +.. code-block:: sh + + mamba install -c conda-forge pytest openmc + Fork the GEOUNED-org/GEOUNED repository by clicking this link, unchecking the Copy the main branch only check box and clicking create fork `https://github.com/GEOUNED-org/GEOUNED/fork `_ From 08fc43bb53dd15f44563e7e2f013c38a9d25597c Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Sat, 15 Jun 2024 13:56:02 +0200 Subject: [PATCH 23/27] Update reverse to JSON and rename to CsgToCad (#229) * moved two methods out of start * removed unnecessary logger line * started CsgToCad class definition * added class * added cli for csgtocad method * restructure docs * adding csgtocad to docs * improved csg to cad examples * format * corrected json path * moved doc string to method * corrected file path * quotes used in dict * removed old code --- .github/workflows/ci.yml | 16 ++- docs/developer_guide.rst | 3 + docs/python_api.rst | 6 +- docs/usage/index.rst | 6 +- ...sage.rst => python_cadtocsg_api_usage.rst} | 4 +- ...sage.rst => python_cadtocsg_cli_usage.rst} | 7 +- docs/usage/python_csgtocad_api_usage.rst | 41 +++++++ docs/usage/python_csgtocad_cli_usage.rst | 78 ++++++++++++ pyproject.toml | 1 + src/geouned/GEOReverse/__init__.py | 1 + src/geouned/GEOReverse/core.py | 113 ++++++++++++++++++ src/geouned/GEOReverse/reverse.py | 71 ----------- src/geouned/GEOReverse/scripts/__init__.py | 0 .../GEOReverse/scripts/geouned_csgtocad.py | 38 ++++++ ...=> config_cadtocsg_complete_defaults.json} | 0 ...imal.json => config_cadtocsg_minimal.json} | 0 ...json => config_cadtocsg_non_defaults.json} | 0 tests/config_csgtocad_complete.json | 13 ++ tests/config_csgtocad_minimal.json | 6 + tests/csg_files/cylinder_box.mcnp | 87 ++++++++++++++ tests/csg_files/cylinder_box.xml | 30 +++++ tests/{test_convert.py => test_cadtocsg.py} | 4 +- tests/test_csgtocad.py | 28 +++++ 23 files changed, 467 insertions(+), 86 deletions(-) rename docs/usage/{python_api_usage.rst => python_cadtocsg_api_usage.rst} (97%) rename docs/usage/{python_cli_usage.rst => python_cadtocsg_cli_usage.rst} (95%) create mode 100644 docs/usage/python_csgtocad_api_usage.rst create mode 100644 docs/usage/python_csgtocad_cli_usage.rst create mode 100644 src/geouned/GEOReverse/core.py delete mode 100644 src/geouned/GEOReverse/reverse.py create mode 100644 src/geouned/GEOReverse/scripts/__init__.py create mode 100644 src/geouned/GEOReverse/scripts/geouned_csgtocad.py rename tests/{config_complete_defaults.json => config_cadtocsg_complete_defaults.json} (100%) rename tests/{config_minimal.json => config_cadtocsg_minimal.json} (100%) rename tests/{config_non_defaults.json => config_cadtocsg_non_defaults.json} (100%) create mode 100644 tests/config_csgtocad_complete.json create mode 100644 tests/config_csgtocad_minimal.json create mode 100644 tests/csg_files/cylinder_box.mcnp create mode 100644 tests/csg_files/cylinder_box.xml rename tests/{test_convert.py => test_cadtocsg.py} (97%) create mode 100644 tests/test_csgtocad.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e9bf6e1..fab46788 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,18 +53,24 @@ jobs: geouned_cadtocsg --help python -c 'import geouned' python -c 'from geouned import CadToCsg' - python -c 'from geouned.GEOReverse import reverse' + python -c 'from geouned import CsgToCad' python -m pip install .[tests] - name: testing package version run: | python -m pytest -v tests/test_version.py - - name: testing GEOUNED functionality + - name: testing GEOUNED CadToCsg functionality run: | - python -m pytest -v tests/test_convert.py - geouned_cadtocsg -i tests/config_complete_defaults.json - geouned_cadtocsg -i tests/config_non_defaults.json + python -m pytest -v tests/test_cadtocsg.py + geouned_cadtocsg -i tests/config_cadtocsg_complete_defaults.json + geouned_cadtocsg -i tests/config_cadtocsg_non_defaults.json + + - name: testing GEOUNED CsgToCad functionality + run: | + python -m pytest -v tests/test_csgtocad.py + geouned_csgtocad -i tests/config_csgtocad_complete.json + geouned_csgtocad -i tests/config_csgtocad_minimal.json - name: install openmc if: ${{ matrix.os == 'ubuntu-latest'}} diff --git a/docs/developer_guide.rst b/docs/developer_guide.rst index eeea924e..4442c148 100644 --- a/docs/developer_guide.rst +++ b/docs/developer_guide.rst @@ -138,16 +138,19 @@ However we need one more dependency to run the tests. Then we can run the tests with the following command from the root of the repository. .. code-block:: sh + python -m pytest We can run individual test files by specifying the file path .. code-block:: sh + python -m pytest tests/test_convert.py We can run individual test functions by specifying the file path and function name .. code-block:: sh + python -m pytest tests/test_convert.py -k 'test_conversion' Additional pytest options that might be useful are including -s for standard output and -vv for very verbose output. diff --git a/docs/python_api.rst b/docs/python_api.rst index 12fa2c9e..72e5f834 100644 --- a/docs/python_api.rst +++ b/docs/python_api.rst @@ -4,6 +4,10 @@ Python API reference .. currentmodule:: geouned +.. autoclass:: CsgToCad + :members: + :show-inheritance: + .. autoclass:: CadToCsg :members: :show-inheritance: @@ -22,4 +26,4 @@ Python API reference .. autoclass:: Tolerances :members: - :show-inheritance: + :show-inheritance: \ No newline at end of file diff --git a/docs/usage/index.rst b/docs/usage/index.rst index 97282c13..157a09ab 100644 --- a/docs/usage/index.rst +++ b/docs/usage/index.rst @@ -10,5 +10,7 @@ GEOUNED can be used as a Python package with the API or as a command line tool w :numbered: :maxdepth: 1 - python_api_usage - python_cli_usage \ No newline at end of file + python_cadtocsg_api_usage + python_cadtocsg_cli_usage + python_csgtocad_api_usage + python_csgtocad_cli_usage diff --git a/docs/usage/python_api_usage.rst b/docs/usage/python_cadtocsg_api_usage.rst similarity index 97% rename from docs/usage/python_api_usage.rst rename to docs/usage/python_cadtocsg_api_usage.rst index 43462eaf..eefc973e 100644 --- a/docs/usage/python_api_usage.rst +++ b/docs/usage/python_cadtocsg_api_usage.rst @@ -1,5 +1,5 @@ -Python Package Usage -==================== +Python Package Usage, CAD to CSG conversion +=========================================== The main class is ``CadToCsg()`` which converts CAD geometry to Constructive Solid Geometry (CSG). There are many arguments that can be passed into the ``CadToCsg()`` class which are documented in the `Python API reference section <../python_api.html>`_ of the documentation. diff --git a/docs/usage/python_cli_usage.rst b/docs/usage/python_cadtocsg_cli_usage.rst similarity index 95% rename from docs/usage/python_cli_usage.rst rename to docs/usage/python_cadtocsg_cli_usage.rst index 1cf983f0..ffdd2c83 100644 --- a/docs/usage/python_cli_usage.rst +++ b/docs/usage/python_cadtocsg_cli_usage.rst @@ -1,7 +1,7 @@ -Command Line Tool Usage -======================= +Command Line Tool Usage, CAD to CSG conversion +============================================== -GEOUNED can be used in the command line. +GEOUNED CAD to CSG conversion can be performed in the command line. These examples assumes you have a CAD STEP file in the current working directory of the terminal called "cuboid.stp" @@ -112,6 +112,7 @@ Here is a complete JSON file specification "cellSummaryFile": true } } + Note that JSON requires ```null``` to be passed in which gets translated to ```None``` in Python. This is converted in the same way as the minimal JSON config file diff --git a/docs/usage/python_csgtocad_api_usage.rst b/docs/usage/python_csgtocad_api_usage.rst new file mode 100644 index 00000000..1c9b2987 --- /dev/null +++ b/docs/usage/python_csgtocad_api_usage.rst @@ -0,0 +1,41 @@ +Python Package Usage, CSG to CAD conversion +=========================================== + +The main class is ``CsgToCad()`` which converts Constructive Solid Geometry (CSG) to CAD. +There are a few arguments that can be passed into the ``CsgToCad().export_cad()`` method which are documented in the `Python API reference section <../python_api.html>`_ of the documentation. + + +If you have install GEOUNED and FreeCAD into your system Python then you can simply run a .py script with Python. +The most minimal use case below shows GEOUNED being imported and the CsgToCad being used to convert a CSG geometry into a STEP CAD file. +The example makes use of default attributes. + +.. code-block:: python + + import geouned + + geo = geouned.CsgToCad() + + geo.export_cad( + csg_format='openmc_xml', + input_filename='cylinder_box.xml', + ) + + +Users can change the default arguments to suit the conversion desired. +The following example shows a usage with every attributes specified. +Remember that the arguments are described in the `Python API reference section <../python_api.html>`_ of the documentation. + +.. code-block:: python + + import geouned + + geo = geouned.CsgToCad() + + geo.export_cad( + input_filename='cylinder_box.xml', + csg_format='openmc_xml', + bounding_box=[-1000.0, -500.0, -1000.0, 0,0,0.0 ], + cell_range_type='exclude', + cell_range=(2,3,4), + output_filename='openmc_xml', + ) diff --git a/docs/usage/python_csgtocad_cli_usage.rst b/docs/usage/python_csgtocad_cli_usage.rst new file mode 100644 index 00000000..83d955e7 --- /dev/null +++ b/docs/usage/python_csgtocad_cli_usage.rst @@ -0,0 +1,78 @@ +Command Line Tool Usage, CSG to CAD conversion +============================================== + +GEOUNED CSG to CAD conversion can be performed in the command line. + +Both OpenMC XML CSG and MCNP CSG formats are supported. + +The first example assumes you have an OpenMC XML CSG file in the current working directory of the terminal called "cylinder_box.xml". + +The most minimal use case below shows a minimal config_openmc.json file being used. + +First create a JSON file called "config_openmc.json" containing the following. + +.. code-block:: json + + { + "export_cad":{ + "input_filename": "cylinder_box.xml", + "csg_format": "openmc_xml" + } + } + + +Then execute the command line interface tool to convert your OpenMC XML CSG file to a STEP CAD file with the default configuration. + +.. code-block:: bash + + geouned_csgtocad -i config_openmc.json + +MCNP CSG files can also be converted, this example assumes you have a MCNP CSG file in te current working directory called "cylinder_box.mcnp". + +For MCNP CSG files, the JSON config file should be as follows and is assumed to be named "config_mcnp.json". + + +.. code-block:: json + + { + "export_cad":{ + "input_filename": "cylinder_box.mcnp", + "csg_format": "mcnp" + } + } + +Then execute the command line interface tool to convert your MCNP CSG file to a STEP CAD file with the default configuration. + +.. code-block:: bash + + geouned_csgtocad -i config_mcnp.json + + +The following example shows a usage with every attributes specified in the config.json file. + +The contents of the JSON file closely matches the Class arguments and method arguments when using the Python package. + +For a full description of each keyword see the `Python API reference section <../python_api.html>`_ of the documentation. + +Here is a complete JSON file specification + +.. code-block:: json + + { + "export_cad":{ + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml", + "output_filename" : "cad_from_csg", + "bounding_box": [-1000, -1000, -1000, 1000, 1000, 1000], + "universe_start": 0, + "level_max": "all", + "cell_range_type": "all", + "cell_range": [], + "mat_range_type": "all", + "mat_range": [] + } + } + +.. code-block:: bash + + geouned_csgtocad -i config.json diff --git a/pyproject.toml b/pyproject.toml index f8fcb9f1..949329ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ docs = [ [project.scripts] geouned_cadtocsg = "geouned.GEOUNED.scripts.geouned_cadtocsg:main" +geouned_csgtocad = "geouned.GEOReverse.scripts.geouned_csgtocad:main" [tool.black] line-length = 128 diff --git a/src/geouned/GEOReverse/__init__.py b/src/geouned/GEOReverse/__init__.py index e69de29b..09671254 100644 --- a/src/geouned/GEOReverse/__init__.py +++ b/src/geouned/GEOReverse/__init__.py @@ -0,0 +1 @@ +from .core import CsgToCad diff --git a/src/geouned/GEOReverse/core.py b/src/geouned/GEOReverse/core.py new file mode 100644 index 00000000..6bf59b62 --- /dev/null +++ b/src/geouned/GEOReverse/core.py @@ -0,0 +1,113 @@ +import typing +import FreeCAD +import Import + +from pathlib import Path + +from .Modules.buildCAD import buildCAD, makeTree +from .Modules.MCNPinput import McnpInput +from .Modules.Objects import CadCell +from .Modules.XMLinput import XmlInput + + +class CsgToCad: + """Base class for the conversion of CSG to CAD models""" + + def __init__(self): + pass + + def export_cad( + self, + input_filename: str, + csg_format: str, + output_filename: str = "cad_from_csg", + bounding_box: typing.Tuple[int, int, int, int, int, int] = (-1000, -1000, -1000, 1000, 1000, 1000), + universe_start: int = 0, + level_max: str = "all", + cell_range_type: str = "all", + cell_range: typing.Tuple[int] = (), + mat_range_type: str = "all", + mat_range: typing.Tuple[int] = (), + # TODO add these to the method signature + # splitTolerance in the Options class + # mat = this is in the CADselection dictionary but not in the docs https://github.com/GEOUNED-org/GEOUNED/blob/76ef697c7dca6a828c7498996ff3313859c872f2/docs/User_Guide_GEOUNED_v2.0.pdf + ): + """export the CSG geometry in OpenMC or MCNP format to a CAD model. + + Args: + input_filename (str): The filename and path of the input CSG text file. + csg_format (str): The format of the CSG input file, options are 'openmc_xml' or 'mcnp' + output_filename (str, optional): The filename stem and path of the output file created. + Two files will be created with the '.step' suffix and one with the 'FCStd' suffix. + Defaults to 'cad_from_csg'. + bounding_box (typing.Tuple[int, int, int, int, int, int], optional): The bounding box + coordinates of the CSG geometry. This should encompass the entire CSG geometry. + Format is (xmin, ymin, zmin, xmax, ymax, zmax) Defaults to (-1000, -1000, -1000, + 1000, 1000, 1000). + universe_start (int, optional): The Universe ID to be converted to CAD. If universe_start + is left as 0 then all the universes and any nested universes are converted. If set + then the universe and all its nested universes are converted. Defaults to 0. + level_max (str, optional): Level maximum of nested universe to be translated. If + level_max < highest nested universe level, cells inside the container cell whose + level is level_max will not be translated. This container cell will be the CAD + solid written in the CAD file. Defaults to "all". + cell_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the cells with any cell ID being converted (range a no effect). + setting to 'include' results in only cells defined in 'range' being converted. + setting to 'exclude' results in exclude all cells defined in range. Defaults to "all". + cell_range (typing.Tuple[int], optional): list of cells to be included/excluded for the conversion. + Defaults to (). + mat_range_type (str, optional): Define how to consider the range values. + setting to 'all' results in all the materials with any cell ID being converted (range a no effect). + setting to 'include' results in only materials defined in 'range' being converted. + setting to 'exclude' results in exclude all materials defined in range. Defaults to "all". + mat_range (typing.Tuple[int], optional): list of materials to be included/excluded for the conversion. + Defaults to (). + + Raises: + ValueError: If the csg_format is not 'openmc_xml' or 'mcnp' then a ValueError is raised. + """ + + # TODO check file extensions are correct + # if Path(output_filename).suffix not in ['.stp', '.step']: + # raise ValueError(f"output file must have a .stp or .step extension, not {universe_start.suffix}") + + # get geometry definition from OpenMC XML or MCNP input + if csg_format == "mcnp": + geo = McnpInput(input_filename) + elif csg_format == "openmc_xml": + geo = XmlInput(input_filename) + else: + msg = f"input format type {csg_format} is not supported. Supported options are 'openmc_xml' or 'mcnp'" + raise ValueError(msg) + + Path(output_filename).parent.mkdir(parents=True, exist_ok=True) + + UnivCell = CadCell() + UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*bounding_box)) + + # TODO make these variable names lower case in the downstream code + + CADselection = { + "Ustart": universe_start, + "levelMax": level_max, + "cell": [cell_range_type, cell_range], + "mat": [mat_range_type, mat_range], + "format": csg_format, + "cell_range_type": cell_range_type, + "cell_range": cell_range, + "mat_range_type": mat_range_type, + "mat_range": mat_range, + } + + # TODO don't return fails variable, just fail in the method and raise the error there + CADCells, fails = buildCAD(UnivCell, geo, CADselection) + + if fails: + print("failed in conversion", fails) + + CADdoc = FreeCAD.newDocument("converted_with_geouned") + + makeTree(CADdoc, CADCells) + Import.export(CADdoc.Objects[0:1], f"{output_filename}.step") + CADdoc.saveAs(f"{output_filename}.FCStd") diff --git a/src/geouned/GEOReverse/reverse.py b/src/geouned/GEOReverse/reverse.py deleted file mode 100644 index faaf432a..00000000 --- a/src/geouned/GEOReverse/reverse.py +++ /dev/null @@ -1,71 +0,0 @@ -import FreeCAD -import Import - -from .CodeVersion import * -from .Modules.buildCAD import buildCAD, makeTree -from .Modules.MCNPinput import McnpInput -from .Modules.Objects import CadCell -from .Modules.processInp import setOptions -from .Modules.XMLinput import XmlInput - - -def reverse(optFile="configRevese.ini"): - - printCodeVersion() - - setting = setOptions(optFile) - - geomfile = setting["fileIn"] - outname = setting["fileOut"] - outBox = setting["outBox"] - inFormat = setting["inFormat"] - - CADselection = { - "Ustart": setting["UStart"], - "levelMax": setting["levelMax"], - "cell": setting["cell"], - "mat": setting["mat"], - "format": setting["inFormat"], - } - - UnivCell = CadCell() - UnivCell.shape = UnivCell.makeBox(FreeCAD.BoundBox(*outBox)) - - # get geometry definition from MCNP input - if inFormat == "mcnp": - geom = McnpInput(geomfile) - elif inFormat == "openmc_xml": - geom = XmlInput(geomfile) - else: - msg = f"input format type {inFormat} is not supported." 'Supported options are "mcnp" or "openmc_xml"' - raise ValueError(msg) - - CADCells, fails = buildCAD(UnivCell, geom, CADselection) - - if fails: - print("failed in conversion", fails) - - CADdoc = FreeCAD.newDocument("WorkingDoc") - - makeTree(CADdoc, CADCells) - Import.export(CADdoc.Objects[0:1], outname + ".stp") - CADdoc.saveAs(outname + ".FCStd") - - -def printCodeVersion(): - - FreeCAD_Version = "{V[0]:}.{V[1]:}.{V[2]:}".format(V=FreeCAD.Version()) - title = """\ -######################################################################### -# # -# GEOReverse version {:<11}{}{:>26} -# FreeCAD version {:<11}{:>36} -# # -#########################################################################""".format( - GEOReverse_Version, GEOReverse_ReleaseDate, "#", FreeCAD_Version, "#" - ) - print(title) - - -if __name__ == "__main__": - reverse() diff --git a/src/geouned/GEOReverse/scripts/__init__.py b/src/geouned/GEOReverse/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/geouned/GEOReverse/scripts/geouned_csgtocad.py b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py new file mode 100644 index 00000000..1288d9f6 --- /dev/null +++ b/src/geouned/GEOReverse/scripts/geouned_csgtocad.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +""" +Convert Cad geometry to CSG format for use in Monte Carlo Codes +""" + +import argparse +import json + +from pathlib import Path + +import geouned + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument( + "-i", + "--input", + type=str, + default="config.json", + help="The path to the config JSON file", +) +args = parser.parse_args() + + +def main(): + + if not Path(args.input).exists(): + raise FileNotFoundError(f"config file {args.input} not found") + + with open(args.input) as f: + config = json.load(f) + + geo = geouned.CsgToCad() + geo.export_cad(**config["export_cad"]) + + +if __name__ == "__main__": + main() diff --git a/tests/config_complete_defaults.json b/tests/config_cadtocsg_complete_defaults.json similarity index 100% rename from tests/config_complete_defaults.json rename to tests/config_cadtocsg_complete_defaults.json diff --git a/tests/config_minimal.json b/tests/config_cadtocsg_minimal.json similarity index 100% rename from tests/config_minimal.json rename to tests/config_cadtocsg_minimal.json diff --git a/tests/config_non_defaults.json b/tests/config_cadtocsg_non_defaults.json similarity index 100% rename from tests/config_non_defaults.json rename to tests/config_cadtocsg_non_defaults.json diff --git a/tests/config_csgtocad_complete.json b/tests/config_csgtocad_complete.json new file mode 100644 index 00000000..33798df9 --- /dev/null +++ b/tests/config_csgtocad_complete.json @@ -0,0 +1,13 @@ +{ + "export_cad": { + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml", + "output_filename": "cad_from_csg", + "bounding_box": [-1000, -1000, -1000, 1000, 1000, 1000], + "universe_start": 0, + "level_max": "all", + "cell_range_type": "all", + "cell_range": [1,2], + "mat_range_type": "all" + } +} \ No newline at end of file diff --git a/tests/config_csgtocad_minimal.json b/tests/config_csgtocad_minimal.json new file mode 100644 index 00000000..7b195bcb --- /dev/null +++ b/tests/config_csgtocad_minimal.json @@ -0,0 +1,6 @@ +{ + "export_cad":{ + "input_filename": "tests/csg_files/cylinder_box.xml", + "csg_format": "openmc_xml" + } +} \ No newline at end of file diff --git a/tests/csg_files/cylinder_box.mcnp b/tests/csg_files/cylinder_box.mcnp new file mode 100644 index 00000000..098038a3 --- /dev/null +++ b/tests/csg_files/cylinder_box.mcnp @@ -0,0 +1,87 @@ +Converted with GEOUNED +C ______ _______ _____ _ _ __ _ _______ ______ +C | ____ |______ | | ___ | | | \ | |______ | \ +C |_____| |______ |_____| |_____| | \_| |______ |_____/ +C Version : 1.0.2.dev38+ga3fd78f.d20240613 +C FreeCAD Version : 0.21.2 +C +C ************************************************************* +C Original Step file : /home/j/GEOUNED/testing/inputSTEP/cylBox.stp +C +C Creation Date : 2024-06-13 14:01:29.607259 +C Solid Cells : 1 +C Total Cells : 4 +C Surfaces : 23 +C Materials : 0 +C +C ************************************************************** +1 0 -14 -6 5:2 6 7 8 9 -1:3 10 11 12 15 16 -4:-15 -13 -10:-15 10 11: + -15 -11 -9 + imp:n=1.0 imp:p=1.0 +C +C ########################################################## +C VOID CELLS +C ########################################################## +C +2 0 17 -18 19 -20 21 -22 (15:-10:-11) (15:11:9) (-8:-7:-2:1:-6:-9) (13: + 15:10) (14:-5:6) (-16:-12:4:-3:-10:-11:-15) + imp:n=1.0 imp:p=1.0 + $Automatic Generated Void Cell. Enclosure(-76.966, -47.115, -22.500, -11.500, -56.992, -40.592) + $Enclosed cells : (1) +3 0 -23 (-17:18:-19:20:-21:22) + imp:n=1.0 imp:p=1.0 + $Graveyard_in +4 0 23 + imp:n=0 imp:p=0 + $Graveyard + +C ########################################################## +C SURFACE DEFINITION +C ########################################################## +1 PY -1.2500035e+01 +2 PY -2.1500035e+01 +3 PY -1.9500035e+01 +4 PY -1.4500035e+01 +5 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -8.8839591e+01 +6 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -8.1689591e+01 +7 P -3.2226569e-01 -5.6149002e-11 9.4664926e-01 -3.1338638e+01 +8 P 3.2226569e-01 5.6149002e-11 -9.4664926e-01 2.2338638e+01 +9 P -9.4664926e-01 -9.5825876e-09 -3.2226569e-01 7.0239591e+01 +10 P -9.4664926e-01 -9.5825876e-09 -3.2226569e-01 6.3039591e+01 +11 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -7.0039591e+01 +12 P -3.2226569e-01 -5.6149002e-11 9.4664926e-01 -3.4238638e+01 +13 P 9.4664926e-01 9.5825876e-09 3.2226569e-01 -6.1239591e+01 +14 GQ 0.103855177195422 0.999999999999999 0.896144822804578 + -0.000000018142699 -0.000000006176279 -0.610145160974434 + -17.298344774043834 34.000069238333026 50.813553217939962 + 1006.753657691386934 +15 GQ 0.103855177195422 0.999999999999999 0.896144822804578 + -0.000000018142699 -0.000000006176279 -0.610145160974434 + -17.298344774043830 34.000069238333033 50.813553217939955 + 995.251157691386766 +16 P 3.2226569e-01 5.6276909e-11 -9.4664926e-01 2.9633723e+01 +17 PX -7.6966386e+01 +18 PX -4.7114745e+01 +19 PY -2.2500035e+01 +20 PY -1.1500035e+01 +21 PZ -5.6992451e+01 +22 PZ -4.0592261e+01 +23 S -6.2040565e+01 -1.7000035e+01 -4.8792356e+01 1.8254058e+01 + +C +MODE P +VOID +NPS 1e6 +PRDMP 2J -1 +C SDEF PAR=P X=D1 Y=D2 Z=D3 +C SI1 -7.6966386e+01 -4.7114745e+01 +C SI2 -2.2500035e+01 -1.1500035e+01 +C SI3 -5.6992451e+01 -4.0592261e+01 +C SP1 0 1 +C SP2 0 1 +C SP3 0 1 +SDEF PAR=P NRM=-1 SUR=23 WGT=1.0468121e+03 DIR=d1 +SI1 0 1 +SP1 -21 1 +F4:P 1 +SD4 1.5208150e+03 \ No newline at end of file diff --git a/tests/csg_files/cylinder_box.xml b/tests/csg_files/cylinder_box.xml new file mode 100644 index 00000000..f62c11f0 --- /dev/null +++ b/tests/csg_files/cylinder_box.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_convert.py b/tests/test_cadtocsg.py similarity index 97% rename from tests/test_convert.py rename to tests/test_cadtocsg.py index a84be0ad..26137237 100644 --- a/tests/test_convert.py +++ b/tests/test_cadtocsg.py @@ -129,7 +129,7 @@ def test_conversion(input_step_file): @pytest.mark.parametrize( "input_json_file", - ["tests/config_complete_defaults.json", "tests/config_minimal.json"], + ["tests/config_cadtocsg_complete_defaults.json", "tests/config_cadtocsg_minimal.json"], ) def test_cad_to_csg_from_json_with_defaults(input_json_file): @@ -156,7 +156,7 @@ def test_cad_to_csg_from_json_with_non_defaults(): for suffix in suffixes: Path("csg").with_suffix(suffix).unlink(missing_ok=True) - my_cad_to_csg = geouned.CadToCsg.from_json("tests/config_non_defaults.json") + my_cad_to_csg = geouned.CadToCsg.from_json("tests/config_cadtocsg_non_defaults.json") assert isinstance(my_cad_to_csg, geouned.CadToCsg) assert my_cad_to_csg.filename == "testing/inputSTEP/BC.stp" diff --git a/tests/test_csgtocad.py b/tests/test_csgtocad.py new file mode 100644 index 00000000..79ae59d1 --- /dev/null +++ b/tests/test_csgtocad.py @@ -0,0 +1,28 @@ +from pathlib import Path +import pytest +import geouned + + +@pytest.mark.parametrize("csg_format", ["mcnp", "openmc_xml"]) +def test_cylbox_convertion(csg_format): + + if csg_format == "openmc_xml": + suffix = ".xml" + elif csg_format == "mcnp": + suffix = ".mcnp" + + geo = geouned.CsgToCad() + + geo.export_cad( + # csg file was made from testing/inputSTEP/cylBox.stp + input_filename=f"tests/csg_files/cylinder_box{suffix}", + csg_format=csg_format, + bounding_box=[-1000.0, -500.0, -1000.0, 0, 0, 0.0], + # TODO add tests for these args that counts volumes in cad file + # cell_range_type='exclude', + # cell_range=(2,3,4), + output_filename=f"tests_outputs/csgtocad/{csg_format}", + ) + + assert Path(f"tests_outputs/csgtocad/{csg_format}.step").exists() + assert Path(f"tests_outputs/csgtocad/{csg_format}.FCStd").exists() From b2ab4fe5a56166717c35d9509f72a6aa1a8459d8 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Sat, 15 Jun 2024 13:57:42 +0200 Subject: [PATCH 24/27] Removing old config ini file based scripts (#226) * removed old scripts * retrigger ci --- README.md | 1 + scripts/colorCADmat.py | 51 --------------- scripts/config.ini | 120 ------------------------------------ scripts/configReverse.ini | 29 --------- scripts/geouned | 47 -------------- scripts/geouned.py | 48 --------------- scripts/geounedClassCall.py | 26 -------- 7 files changed, 1 insertion(+), 321 deletions(-) delete mode 100644 scripts/colorCADmat.py delete mode 100644 scripts/config.ini delete mode 100644 scripts/configReverse.ini delete mode 100644 scripts/geouned delete mode 100644 scripts/geouned.py delete mode 100644 scripts/geounedClassCall.py diff --git a/README.md b/README.md index ed33239e..d1d0e6a9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/downloads.svg)](https://anaconda.org/conda-forge/geouned) # GEOUNED + A tool to convert from CAD to CSG & CSG to CAD for Monte Carlo transport codes (MCNP & OpenMC). This repository contains the implementation of the algorithm presented in the paper [GEOUNED: A new conversion tool from CAD to Monte Carlo geometry](https://doi.org/10.1016/j.net.2024.01.052). diff --git a/scripts/colorCADmat.py b/scripts/colorCADmat.py deleted file mode 100644 index 5185c9a9..00000000 --- a/scripts/colorCADmat.py +++ /dev/null @@ -1,51 +0,0 @@ -import re -import sys - -sys.path.append("/usr/lib64/freecad/lib64/") -import ImportGui - -bd = 255 -gd = 255 * 255 -rd = 255 * 255 * 255 -matNumber = re.compile(r"Material_(?P\d+)") - - -def getColor(num, vmin=0, vmax=rd): - colRange = rd - dnum = vmax - vmin - dx = num - vmin - scale = colRange / dnum - snum = dx * scale + 1 - - red = snum // gd - r = snum - red * gd - green = r // bd - blue = r - green * bd - return (red / 255, green / 255, blue / 255) - - -def setColorMaterial(documents): - featureObj = [] - matMin = 999999999 - matMax = 0 - for obj in documents.Objects: - if obj.TypeId == "Part::Feature": - for o in obj.InListRecursive: - m = matNumber.search(o.Label) - if m: - mat = int(m.group("matnum")) - matMin = min(matMin, mat) - matMax = max(matMax, mat) - featureObj.append((obj, mat)) - - for obj, mat in featureObj: - obj.ViewObject.ShapeColor = getColor(mat, matMin, matMax) - - -# color solids in a Freecad document with respect to the material number -doc = App.ActiveDocument -setColorMaterial(doc) -name = doc.Label -outname = name + "_color" -ImportGui.export(doc.RootObjects, outname + ".stp") -doc.saveAs(outname + ".FCStd") diff --git a/scripts/config.ini b/scripts/config.ini deleted file mode 100644 index c2bc88cd..00000000 --- a/scripts/config.ini +++ /dev/null @@ -1,120 +0,0 @@ -[Files] -title = title of the model in MCNP input -stepFile = stepfilename.stp - -# base name for output geometry -geometryName = pieza -matFile = materials.txt - -# format of the converted geometry : mcnp, openmc_xml, openmc_py, serpent, phits -outFormat = mcnp, openmc_py, openmc_xml - - -[Parameters] -# assume multi component solid as one solid cell -compSolids = True - -# write SDEF card for stochatic volume calculation -volSDEF = False - -# write the solid volume in mcnp cell definition (only for CAD solids) -volCARD = False - -# write the material cards associated to material label present in the cell definition. -# dummy material definition is hydrogen atom "Mx 1001 1" -dummyMat = False - -# write the universe card on each cell of the mcnp output -#UCARD = 101 - -# initial value for cell numbering -startCell = 1 - -# initial value for surface numbering -startSurf = 1 - -# mininal size of the void cells -minVoidSize = 100 - -# material parameters to assign to all void cells -#voidMat = (100,1e-3,'Air assigned to Void') - -# select cells ranges in the CAD model to translate -#cellRange = (0,100) - -# print out the cell summary file -cellSummaryFile = True - -# print out the cell comments file -cellCommentFile = True - -# Boolean simplification of the cell definition. Options are -# No : no simplification (default) -# void : simplify only void cells with faster method than "full" option. The optimization is not so high as full option -# voidfull : simplify only void cells with a better optimization. -# full : simplify all void and solid cells with the better optimization. -# full simplification may reduce a 30%-40% the size of the mcnp output in term of memory requirement. -# full simplification lasts 3-4 more time than without any simplification. -#simplify = full - -# export CAD solid after reading. (stop execution after reading, no translation) -#exportSolids = 'out.stp' - -# if enclosures are defined in the CAD models, the voids cells of the enclosure are located in the mcnp outputfile -# in the location where the enclosure solid is located in the CAD solid tree. -#sort_enclosure =True - - -[Tolerances] -# change default tolerance values -relativeTol = False -# define the general tolerance is the geouned conversion process -distance = 1e-4 -angle = 1e-4 - -# define the tolerance when comparing surfaces to check if there are the same surface -# keywords are : pln_distance, cyl_distance, kne_distance, tor_distance, sph_distance -# pln_angle, cyl_angle, kne_angle, tor_angle -#pln_distance = 1e-2 -#pln_angle = 1e-2 - - -[MCNP_Numeric_Format] -# change default numeric format for numbers in MCNP input (float: X.Xf or scientific: X.Xe) -#P_abc = 14.7f -#P_d = 14.7f -#P_xyz = 14.7f - - -[Options] -# force to use cylinder instead of cone when additional surface should be added to Torus surface in the mcnp cell definition -forceCylinder = False - -# new method for plane splitting. -# During the decomposition phase all plane types PX,PY,PZ,P are considered, and parellel planes are group together. -# The solid decomposition is performing by splitting first with larger group of planes. -# The smallest solid decomposition sometimes is obtained spliting first with the larger group of plane sometimes smaller . -# It has been observed that in general the smallest decomposition is obtained decomposing first with largest group of parallel plane, -# but when the number of parallel plane in a group is very large it is better to decompose first with the group a smaller parallel planes -# The code will decompose first with the largest group of parallel planes unless the number of planes in this group is highest than the -# parameter "nPlaneRevese" in this case, the decomposition will be perfom by splitting with the smallest number of parallel planes. -# by defaul nPlaneReverse = 10 - -newSplitPlane = True -nPlaneReverse = 10 -splitTolerance = 0 - -# Use specific conversion module for geometry defined with only cells defined by planes. -# Cells cannot have "hooked" shape -# Default value False -# Facets = True - -# write ion MCNP format output plane definition with no-aligned 3 points belonging to the plane -# this option should be used with Facets=True option -# Default value False -# prnt3PPlane = True - -# enable a robust cell definition where other solid definition is rested from current solid definition. -# remove some lost particles introduced by numerical precision during transport -# traduction time is longer than full simplification option -# forceNoOverlap = True diff --git a/scripts/configReverse.ini b/scripts/configReverse.ini deleted file mode 100644 index 39555fd5..00000000 --- a/scripts/configReverse.ini +++ /dev/null @@ -1,29 +0,0 @@ -[Setting] -inputFile = my_CSG_model -CADFile = modelCAD - -# CSG format allowed are mcnp or openmc_xml -inFormat = mcnp - -# box dim in cm -# box entries xmin xmax ymin ymax zmin zmax -outBox = -500 500 -500 500 0 1000 - -[Levels] -UStart = 0 -levelMax = all - -# For cell and materials "rangeType" value may be all, include, exclude -# range example is : 2:20, 34, 38, 100:200 -[Cells] -rangeType = all -#range = 1:13 - -[Materials] -rangeType = exclude -range = 0 - -[Options] -splitTolerance = 0.01 - - diff --git a/scripts/geouned b/scripts/geouned deleted file mode 100644 index 254b4095..00000000 --- a/scripts/geouned +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python - -# Path to GEOUNED Package -import sys - -geo_path="/opt/geouned/v1.0/" - -sys.path.append(geo_path) - -# linux distribution -sys.path.append('/usr/lib64/freecad/lib64/') - -from geouned import CadToCsg -from geouned.GEOReverse import reverse - -runReverse = False -if len(sys.argv) < 2 : - inifile = 'config.ini' - -elif len(sys.argv) == 2 : - if sys.argv[1] == '-r': - runReverse = True - inifile = 'configReverse.ini' - else: - inifile = sys.argv[1] - -elif len(sys.argv) == 3: - if sys.argv[1] == '-r': - runReverse = True - inifile = sys.argv[2] - elif sys.argv[2] == '-r': - runReverse = True - inifile = sys.argv[1] - else: - raise ValueError('Bad option') -else: - raise ValueError('Too many input arguments') - - -if not runReverse : - GEO = CadToCsg() - GEO.set_configuration(inifile) - GEO.Start() - -else: - print(inifile) - reverse(inifile) diff --git a/scripts/geouned.py b/scripts/geouned.py deleted file mode 100644 index 0d588b02..00000000 --- a/scripts/geouned.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python - -# Path to GEOUNED Package - - -# only if modules are not in the PYTHONPATH or directly installed in the python distribution used -import sys - -from geouned import CadToCsg -from geouned.GEOReverse import reverse - -# geo_path="C:\\Users\\Juan\\Documents\\work\\GEOUNED\\RepoGit\\GitHub\\GEOUNEDcode\\src" -# sys.path.append(geo_path) -# sys.path.append('C:\\Program Files\\FreeCAD 0.19\\bin...') - - -runReverse = False -if len(sys.argv) < 2: - inifile = "config.ini" - -elif len(sys.argv) == 2: - if sys.argv[1] == "-r": - runReverse = True - inifile = "configReverse.ini" - else: - inifile = sys.argv[1] - -elif len(sys.argv) == 3: - if sys.argv[1] == "-r": - runReverse = True - inifile = sys.argv[2] - elif sys.argv[2] == "-r": - runReverse = True - inifile = sys.argv[1] - else: - raise ValueError("Bad option") - -else: - raise ValueError("Too many input arguments") - -if not runReverse: - GEO = CadToCsg() - GEO.set_configuration(inifile) - GEO.Start() - -else: - print(inifile) - reverse(inifile) diff --git a/scripts/geounedClassCall.py b/scripts/geounedClassCall.py deleted file mode 100644 index f7d182b4..00000000 --- a/scripts/geounedClassCall.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python - -# Path to GEOUNED Package - - -# only if modules are not in the PYTHONPATH or directly installed in the python distribution used -import sys - -geo_path = "C:\\Users\\Patrick\\Documents\\GitHub\\GEOUNED\\src" -sys.path.append(geo_path) -sys.path.append("C:\\Program Files\\FreeCAD 0.19\\bin...") - -from geouned import CadToCsg - -stepFileName = "placa.stp" - -GEO = CadToCsg("Conversion Example") - -GEO.set("stepFile", stepFileName) -GEO.set("geometryName", "Placa") -GEO.set("outFormat", ("mcnp", "openmc_xml")) -GEO.set("planeDistance", 0.05) -GEO.set("quadricPY", True) -GEO.set("P_abc", "12f") -GEO.set("P_d", "12f") -GEO.Start() From f444000fd3c79771b180ad20ad696abd348af671 Mon Sep 17 00:00:00 2001 From: Alex Valentine <40658938+alexvalentine94@users.noreply.github.com> Date: Sun, 16 Jun 2024 15:50:41 +0200 Subject: [PATCH 25/27] closes #213; additional guidance for install (#235) --- docs/install/index.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/install/index.rst b/docs/install/index.rst index 778fec1d..20f3ee08 100644 --- a/docs/install/index.rst +++ b/docs/install/index.rst @@ -8,10 +8,18 @@ The installation selected has implications for how you run GEOUNED Python script Currently the Mamba / Conda install is the recommended method. The main complication when installing GEOUNED is integration between the main dependency (FreeCAD) and the users system Python. Mamba / Conda provides a connection between the FreeCAD Python library and your system Python. -Users have also had success installing FreeCAD and making use of the Python version inbuilt into FreeCAD and the freecad.cmd however the integration ofr FrreCAD with the system Python is more challenging when installing in this manner. +Users have also had success installing FreeCAD and making use of the Python version inbuilt into FreeCAD and the freecad.cmd however the integration of FrreCAD with the system Python is more challenging when installing in this manner. .. TODO as well so these installation methods are listed for completeness. +Note that your fork of the repostory must have releases which conform to PEP440 i.e. semantic versioning. When installing, setuptools +uses these tags to get the release number of GEOUNED. Release versions in the central repository now conform to this standard and can be reflected in your +fork using: +.. code-block:: sh + + git fetch --tags upstream + +where upstream is the remote url of the original repository. .. toctree:: :numbered: From 7791250d4591e4b8d3e0394b008085d9884c4261 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 18 Jun 2024 09:15:32 +0100 Subject: [PATCH 26/27] Update docs badge with new action name (#238) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1d0e6a9..71dcb5f3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![CI testing](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/ci.yml) [![Upload Python Package](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/python-publish.yml) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/psf/black) -[![documentation](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation.yml) +[![documentation release](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation_release.yml/badge.svg)](https://github.com/GEOUNED-org/GEOUNED/actions/workflows/documentation_release.yml) [![PyPI](https://img.shields.io/pypi/v/geouned?&label=PyPI)](https://pypi.org/project/geouned/) [![Anaconda-Server Badge](https://anaconda.org/conda-forge/geouned/badges/version.svg)](https://anaconda.org/conda-forge/geouned) From 1da76724cd8b2ef04d4165ea6ca267bd074884e0 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Tue, 18 Jun 2024 09:17:47 +0100 Subject: [PATCH 27/27] Added version 1.2 to docs versions (#236) --- docs/version_switcher.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/version_switcher.json b/docs/version_switcher.json index 3f86e201..8a66d4f3 100644 --- a/docs/version_switcher.json +++ b/docs/version_switcher.json @@ -8,5 +8,10 @@ "name": "1.1.0", "version": "1.1.0", "url": "https://geouned-org.github.io/GEOUNED/1.1.0" + }, + { + "name": "1.2.0", + "version": "1.2.0", + "url": "https://geouned-org.github.io/GEOUNED/1.2.0" } -] \ No newline at end of file +]