Coverage for fiqus/geom_generators/GeometryHomogenizedConductor.py: 22%

86 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2025-11-29 01:35 +0000

1import os, json, gmsh, logging 

2 

3from fiqus.utils.Utils import GmshUtils 

4from typing import Tuple 

5 

6logger = logging.getLogger('FiQuS') 

7 

8class Rectangle: 

9 """ A class to represent a rectangular surface in gmsh. """ 

10 def __init__( self, x_center, y_center, z_center, width, height, name) -> None: 

11 self.name = name 

12 

13 self.x_center = x_center 

14 self.y_center = y_center 

15 self.z_center = z_center 

16 self.width = width 

17 self.height = height 

18 

19 self.tag = gmsh.model.occ.addRectangle(x_center-width/2, y_center-height/2, z_center, width, height) 

20 self.dimTag: Tuple = (2, self.tag) 

21 

22 

23class Circle: 

24 """ A class to represent a circle surface in gmsh. """ 

25 def __init__(self, x_center, y_center, z_center, radius, name) -> None: 

26 self.name = name 

27 

28 self.x_center = x_center 

29 self.y_center = y_center 

30 self.z_center = z_center 

31 self.radius = radius 

32 

33 self.tag = gmsh.model.occ.addDisk(self.x_center, self.y_center, self.z_center, radius, radius) 

34 self.dimTag: Tuple = (2, self.tag) 

35 

36 

37class HomogenizedConductor: 

38 """ 

39 A class representing the geometry of a 2D cross section containing multiple cables and excitation coils in an isolating medium. 

40 

41 :ivar cables: list of surfaces representing the cable cross sections. 

42 :vartype cables: list[list[Surface]] 

43 :ivar excitation_coils: list of surfaces representing the excitation coils cross sections. 

44 :vartype excitation_coils: list[list[Surface]] 

45 :ivar Air: Surrounding surface representing the air region. 

46 :vartype Air: Surface 

47 """ 

48 def __init__(self, fdm) -> None: 

49 """ 

50 Initializes the HomogenizedConductor object. 

51 """ 

52 self.fdm = fdm 

53 

54 self.cables = self.generate_cables() 

55 self.excitation_coils = self.generate_excitation_coils() 

56 self.air = self.generate_air() 

57 

58 def generate_cables(self): 

59 """  

60 This function generates a list of rectangular surfaces according to the specifications of the cables within the yaml. 

61 

62 :return: List of rectangle surfaces 

63 """ 

64 cables = [] 

65 for idx, cable in enumerate(self.fdm.magnet.geometry.cables): 

66 cables.append(Rectangle(cable.center_position[0],cable.center_position[1],cable.center_position[2], 

67 cable.width, cable.height, name='Cable'+str(idx+1))) 

68 return cables 

69 

70 def generate_excitation_coils(self): 

71 """  

72 This function generates a list of rectangular surfaces according to the specifications of the excitation coils within the yaml. 

73 

74 :return: List of rectangle surfaces 

75 """ 

76 excitation_coils = [] 

77 for idx, excitation_coil in enumerate(self.fdm.magnet.geometry.excitation_coils): 

78 excitation_coils.append(Rectangle(excitation_coil.center_position[0],excitation_coil.center_position[1],excitation_coil.center_position[2], 

79 excitation_coil.width, excitation_coil.height, name='Coil'+str(idx+1))) 

80 return excitation_coils 

81 

82 def generate_air(self): 

83 """ 

84 This function generates the surrounding air surface by fragmenting a base surface with the previously defined cable surfaces. 

85 

86 :raises ValueError: 'circle' is the only defined air surface form 

87 :return: fragmented air surface 

88 """ 

89 if self.fdm.magnet.geometry.air_form == 'circle': 

90 air_pos = self.fdm.magnet.geometry.air.center_position 

91 air = Circle(air_pos[0],air_pos[1],air_pos[2], 

92 self.fdm.magnet.geometry.air.radius, name='Air') 

93 else: 

94 raise ValueError('Undefined air_form.') 

95 # fragment air with cables and excitation coils 

96 gmsh.model.occ.synchronize() 

97 outDimTags, outDimTagsMap = gmsh.model.occ.fragment([(2, air.tag)], [cable.dimTag for cable in self.cables] + [excitation_coil.dimTag for excitation_coil in self.excitation_coils]) 

98 return air 

99 

100 def get_vi_dictionary(self): 

101 """  

102 This function dumps the dimTags of all surfaces into one dictionary, which can be used to generate the volume information file. 

103 

104 :return: dictionary with volume information 

105 """ 

106 vi_dictionary = {} 

107 # add all cables 

108 for cable in self.cables: 

109 vi_dictionary.update({str(cable.name):list(cable.dimTag)}) 

110 # add all excitation coils 

111 for excitation_coil in self.excitation_coils: 

112 vi_dictionary.update({str(excitation_coil.name):list(excitation_coil.dimTag)}) 

113 # add air 

114 vi_dictionary.update({str(self.air.name):list(self.air.dimTag)}) 

115 return vi_dictionary 

116 

117 

118class Geometry: 

119 def __init__(self, fdm, inputs_folder_path, verbose=True) -> None: 

120 """  

121 Initializes the Geometry class. 

122  

123 :param fdm: The fiqus data model. 

124 :type fdm: object 

125 :param inputs_folder_path: The full path to the folder with input files, i.e., conductor and STEP files. 

126 :type inputs_folder_path: str 

127 :param verbose: If True, more information is printed in the Python console. Defaults to True. 

128 :type verbose: bool, optional 

129 """ 

130 self.fdm = fdm 

131 self.inputs_folder = inputs_folder_path 

132 self.geom_folder = os.path.join(os.getcwd()) 

133 self.geom_file = os.path.join(self.geom_folder, f'{fdm.general.magnet_name}.brep') 

134 self.vi_file = os.path.join(self.geom_folder, f'{fdm.general.magnet_name}.vi') 

135 self.verbose = verbose 

136 # start GMSH 

137 self.gu = GmshUtils(self.geom_folder, self.verbose) 

138 self.gu.initialize(verbosity_Gmsh=fdm.run.verbosity_Gmsh) 

139 # To see the surfaces in a better way in GUI: 

140 gmsh.option.setNumber("Geometry.SurfaceType", 2) 

141 

142 def generate_geometry(self): 

143 """ 

144 Generates the geometry of conductors within a surrounding air region as specified in the yaml. 

145 

146 :param gui: If True, launches an interactive GUI after generating the geometry. Default is False. 

147 :type gui: bool, optional 

148 :return: None 

149 """ 

150 logger.info("Generating geometry") 

151 

152 # 1) Either load the geometry from a yaml file or create the model from scratch 

153 self.geometry = HomogenizedConductor(fdm=self.fdm) 

154 

155 gmsh.model.occ.synchronize() 

156 logger.info("Writing geometry") 

157 gmsh.write(self.geom_file) # Write the geometry to a .brep file 

158 

159 def load_geometry(self): 

160 """ Loads geometry from .brep file. """ 

161 logger.info("Loading geometry") 

162 

163 gmsh.clear() 

164 gmsh.model.occ.importShapes(self.geom_file, format="brep") 

165 gmsh.model.occ.synchronize() 

166 

167 def generate_vi_file(self, gui=False): 

168 """ 

169 Generates volume information file. Volume information file stores dimTags of all 

170 the stored volumes. Since this model is 2D, those volumes are equivalent to simple surfaces. 

171 """ 

172 

173 dimTagsDict = self.geometry.get_vi_dictionary() 

174 with open(self.vi_file, 'w') as f: 

175 json.dump(dimTagsDict, f) 

176 

177 if gui: 

178 #self.generate_physical_groups() 

179 self.gu.launch_interactive_GUI() 

180 else: 

181 if gmsh.isInitialized(): 

182 gmsh.clear() 

183 gmsh.finalize()