Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
*.FCStd1
*.pyc
*.msh
.vscode/
.pytest_cache/
.idea/
tmpFolder/
venv/
gmshDoc/

*.msh
*.vtk
*.areas.json
32 changes: 21 additions & 11 deletions src/AreaExporterService.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,34 @@ def addComputedArea(self, geometry:str, label:str, area:float):
}
self.computedAreas['geometries'].append(geometry)

def addPhysicalModelOfDimension(self, mappedElements:Dict[str,str], dimension=2):
physicalGroups = gmsh.model.getPhysicalGroups(dimension)
def addPhysicalModelForConductors(self, mappedElements:Dict[str,str]):
physicalGroups = gmsh.model.getPhysicalGroups(1)
for physicalGroup in physicalGroups:
entityTags = gmsh.model.getEntitiesForPhysicalGroup(*physicalGroup)
geometryName = gmsh.model.getPhysicalName(*physicalGroup)
if not geometryName.startswith("Conductor_"):
continue

label = ''
for key, geometry in mappedElements.items():
if geometry == geometryName:
label = key
for tag in entityTags:
if dimension == 1:
rad = gmsh.model.occ.getMass(dimension, tag) / (2*np.pi)
area = rad*rad*np.pi
if dimension == 2:
area = gmsh.model.occ.getMass(dimension, tag)
if geometryName != AreaExporterService._EMPTY_NAME_CASE:
self.addComputedArea(geometryName, label, area)

break

# Find surface that has these curves as boundaries
allSurfaces = gmsh.model.getEntities(2)
foundSurface = None
for surface in allSurfaces:
boundary = gmsh.model.getBoundary([surface], oriented=False, recursive=False)
boundaryTags = set(tag for dim, tag in boundary)
if set(entityTags) == boundaryTags:
foundSurface = surface
break

if foundSurface:
area = gmsh.model.occ.getMass(2, foundSurface[1])
self.addComputedArea(geometryName, label, area)

def exportToJson(self, exportFileName:str):
with open(exportFileName + ".areas.json", 'w') as f:
json.dump(self.computedAreas, f, indent=3)
1 change: 0 additions & 1 deletion src/Graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def getParentNodes(self) -> List:
def getChildNodes(self) -> List:
return [edge[-1] for edge in self._edges]

#Necesita una revisión pero por ahora hace lo que necesito
def prune_to_longest_paths(self):
connections = self.get_connections()
roots = [n for n in self._nodes if n not in self.getChildNodes()]
Expand Down
195 changes: 132 additions & 63 deletions src/ShapesClassification.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ def __init__(self, shapes, jsonFile:str):
self.crossSectionData = jsonData['CrossSection']
self.pecs = self.get_pecs(shapes)
self.dielectrics = self.get_dielectrics(shapes)
self.open = self.get_open_boundaries(shapes)
self.vacuum = dict()
self.open = dict()
self.nestedGraph = self.__getNestedGraph()
self.isOpenCase = self.isOpenProblem()

Expand All @@ -40,27 +40,36 @@ def getNumberFromName(entity_name: str, label: str):
num = int(entity_name[ini:])
return num

def get_pecs(self, entity_tags) -> Dict[str, Dict[str,any]]:
pecNames = self.__getGeometryNamesByMaterialType('PEC')
pecs = dict()
def get_entities_by_material_type(self, entity_tags, material_type: str, entity_dim: int = 2) \
-> Dict[str, List[Tuple[int,int]]]:
"""
Generic method to extract entities by material type from the cross-section data.

Args:
entity_tags: List of entity tags from gmsh
material_type: The material type to filter by (e.g., 'PEC', 'Dielectric', 'OpenBoundary')
entity_dim: The entity dimension to filter by (default: 2 for surfaces)

Returns:
Dictionary mapping entity names to lists of entity tags
"""
material_names = self.__getGeometryNamesByMaterialType(material_type)
entities = dict()
for s in entity_tags:
name = gmsh.model.get_entity_name(*s).split('/')[-1]
if s[0] != 2 or name not in pecNames:
if s[0] != entity_dim or name not in material_names:
continue
pecs[name] = [s]

return pecs
entities.setdefault(name, []).append(s)
return entities

def get_dielectrics(self, entity_tags) -> Dict[str, Dict[str,any]]:
dielectricNames = self.__getGeometryNamesByMaterialType('Dielectric')
dielectrics = dict()
for s in entity_tags:
name = gmsh.model.get_entity_name(*s).split('/')[-1]
if s[0] != 2 or name not in dielectricNames:
continue
dielectrics[name] = [s]

return dielectrics
def get_pecs(self, entity_tags) -> Dict[str, List[Tuple[int,int]]]:
return self.get_entities_by_material_type(entity_tags, 'PEC')

def get_dielectrics(self, entity_tags) -> Dict[str, List[Tuple[int,int]]]:
return self.get_entities_by_material_type(entity_tags, 'Dielectric')

def get_open_boundaries(self, entity_tags) -> Dict[str, List[Tuple[int,int]]]:
return self.get_entities_by_material_type(entity_tags, 'OpenBoundary')

def __getGeometryNamesByMaterialType(self, materialType:str) -> List[str]:
names = [
Expand All @@ -70,19 +79,25 @@ def __getGeometryNamesByMaterialType(self, materialType:str) -> List[str]:
]
return names

def isOpenProblem(self) -> None:
def isOpenBoundaryDefined(self) -> bool:
return len(self.open) > 0

def isOpenProblem(self) -> bool:
roots = self.nestedGraph.roots
if len(roots) > 1: #Más de un componente pec/pec pec/dielectric dielectric/dielectric etc da al exterior
if len(self.open) == 1:
return True
if roots[0] in self.dielectrics.keys(): #El único root es un dielectrico
if len(roots) > 1:
return True
if roots[0] in self.dielectrics.keys():
return True
return False

def removeConductorsFromDielectrics(self):
conductorsOnlyGraph = self.getConductorOnlyGraph()
for num, diel in self.dielectrics.items():
pec_surfs = []
for num2, pec_surf in self.pecs.items():
if (num2 in self.nestedGraph.roots) and (not self.isOpenCase):
if (num2 in conductorsOnlyGraph.roots) and (not self.isOpenCase):
continue
pec_surfs.extend(pec_surf)
self.dielectrics[num] = gmsh.model.occ.cut(diel, pec_surfs, removeTool=False)[0]
Expand All @@ -105,7 +120,7 @@ def ensureDielectricsDoNotOverlap(self):

def buildVacuumDomain(self):
if self.isOpenCase:
self.vacuum = self._buildDefaultVacuumDomain()
self.vacuum = self._buildOpenVacuumDomain()
else:
self.vacuum = self._buildClosedVacuumDomain()
return self.vacuum
Expand All @@ -126,61 +141,73 @@ def _buildClosedVacuumDomain(self) -> Tuple[int, int]:
gmsh.model.occ.synchronize()
return dict([['Vacuum_0', dom]])

def _buildDefaultVacuumDomain(self):
NEAR_REGION_BOUNDING_BOX_SCALING_FACTOR = 1.25
def _buildOpenVacuumDomain(self):
NEAR_REGION_BOUNDING_BOX_SCALING_FACTOR = 1.15
FAR_REGION_DISK_SCALING_FACTOR = 4.0
nonVacuumSurfaces = []
for _, surf in self.pecs.items():
nonVacuumSurfaces.extend(surf)
for _, surf in self.dielectrics.items():
nonVacuumSurfaces.extend(surf)

if self.isOpenBoundaryDefined():
name, vacuum = next(iter(self.open.items()))

boundingBox = BoundingBox.getBoundingBoxFromGroup(nonVacuumSurfaces)
vacuum = gmsh.model.occ.cut(vacuum, nonVacuumSurfaces,
removeObject=True, removeTool=False)[0]
gmsh.model.occ.synchronize()


bbMaxLength = np.max(boundingBox.getLengths())
nearVacuumBoxSize = bbMaxLength*NEAR_REGION_BOUNDING_BOX_SCALING_FACTOR
nVOrigin = tuple(
np.subtract(boundingBox.getCenter(),
(nearVacuumBoxSize/2.0, nearVacuumBoxSize/2.0, 0.0)))
nearVacuum = [
(2, gmsh.model.occ.addRectangle(*nVOrigin, *(nearVacuumBoxSize,)*2))
]
vacuumBoundaries = gmsh.model.getBoundary(vacuum)
externalVacuumBoundaries = [dt for dt in vacuumBoundaries if dt[1]>0]
self.open = dict([[name, externalVacuumBoundaries]])

farVacuumDiameter = FAR_REGION_DISK_SCALING_FACTOR * boundingBox.getDiagonal()
farVacuum = [(2, gmsh.model.occ.addDisk(
*boundingBox.getCenter(),
farVacuumDiameter, farVacuumDiameter))]
return dict([['Vacuum_0', vacuum]])
else:
boundingBox = BoundingBox.getBoundingBoxFromGroup(nonVacuumSurfaces)

gmsh.model.occ.synchronize()
self.open = dict([['OpenBoundary_0', gmsh.model.getBoundary(farVacuum)]])
bbMaxLength = np.max(boundingBox.getLengths())
nearVacuumBoxSize = bbMaxLength*NEAR_REGION_BOUNDING_BOX_SCALING_FACTOR
nVOrigin = tuple(
np.subtract(boundingBox.getCenter(),
(nearVacuumBoxSize/2.0, nearVacuumBoxSize/2.0, 0.0)))
nearVacuum = [
(2, gmsh.model.occ.addRectangle(*nVOrigin, *(nearVacuumBoxSize,)*2))
]

farVacuum = gmsh.model.occ.cut(
farVacuum, nearVacuum, removeObject=True, removeTool=False)[0]
farVacuumDiameter = FAR_REGION_DISK_SCALING_FACTOR * boundingBox.getDiagonal()
farVacuum = [(2, gmsh.model.occ.addDisk(
*boundingBox.getCenter(),
farVacuumDiameter, farVacuumDiameter))]

gmsh.model.occ.synchronize()
self.open = dict([['OpenBoundary_0', gmsh.model.getBoundary(farVacuum)]])

nearVacuum = gmsh.model.occ.cut(
nearVacuum, nonVacuumSurfaces, removeObject=True, removeTool=False)[0]
farVacuum = gmsh.model.occ.cut(
farVacuum, nearVacuum, removeObject=True, removeTool=False)[0]
nearVacuum = gmsh.model.occ.cut(
nearVacuum, nonVacuumSurfaces, removeObject=True, removeTool=False)[0]

gmsh.model.occ.synchronize()

# -- Set mesh size for near vacuum region
bb = BoundingBox(
gmsh.model.getBoundingBox(2, nearVacuum[0][1]))
minSide = np.min(np.array([bb.getLengths()[0], bb.getLengths()[1]]))
gmsh.model.occ.synchronize()

# -- Set mesh size for near vacuum region
bb = BoundingBox(
gmsh.model.getBoundingBox(2, nearVacuum[0][1]))
minSide = np.min(np.array([bb.getLengths()[0], bb.getLengths()[1]]))

innerRegion = gmsh.model.getBoundary(nearVacuum, recursive=True)
gmsh.model.mesh.setSize(innerRegion, minSide / 20)

gmsh.model.occ.synchronize()

innerRegion = gmsh.model.getBoundary(nearVacuum, recursive=True)
gmsh.model.mesh.setSize(innerRegion, minSide / 20)
return dict([['Vacuum_0', nearVacuum], ['Vacuum_1', farVacuum]])

gmsh.model.occ.synchronize()

return dict([['Vacuum_0', nearVacuum], ['Vacuum_1', farVacuum]])

def __getNestedGraph(self):
gmsh.model.occ.synchronize()
graph = Graph()
elements:Dict = {}
elements = {**self.pecs, **self.dielectrics}
elements = {**self.pecs, **self.dielectrics, **self.open}
for key in elements:
graph.add_node(key)
for i, keyA in enumerate(elements):
Expand All @@ -192,7 +219,7 @@ def __getNestedGraph(self):
removeObject=False,
removeTool=False
)
if len(inter[1][0]) == 0: #comprueba las intersecciones en las que interfiere el objeto
if len(inter[1][0]) == 0:
continue
else:
if inter[1][0] == elements[keyA]:
Expand All @@ -203,16 +230,58 @@ def __getNestedGraph(self):
graph._reorderData()
return graph

def getComponentsMappedByLevel(self) -> Dict[str,str]:
sortedNodes = self.nestedGraph.getNodesByLevels()
def getConductorOnlyGraph(self) -> Graph:
"""
Creates a new graph containing only conductor nodes by removing all dielectric nodes
from the nested graph and preserving conductor relationships.

Returns:
Graph: A new graph with only conductor nodes and their direct connections
"""
conductor_graph = Graph()

for conductor_name in self.pecs.keys():
if conductor_name in self.nestedGraph.nodes:
conductor_graph.add_node(conductor_name)

for edge in self.nestedGraph.edges:
source, destination = edge

if source in self.pecs.keys() and destination in self.pecs.keys():
conductor_graph.add_edge(source, destination)

elif source in self.pecs.keys() and destination in self.dielectrics.keys():
# Look for conductor nodes that are children of this dielectric
for child_edge in self.nestedGraph.edges:
child_source, child_dest = child_edge
if child_source == destination and child_dest in self.pecs.keys():
conductor_graph.add_edge(source, child_dest)

conductor_graph.prune_to_longest_paths()
conductor_graph._reorderData()
return conductor_graph

def getMappedComponents(self) -> Dict[str,str]:

mappedElements = []

conductors = []
dielectrics = []
sortedNodes = self.nestedGraph.getNodesByLevels()
for node in sortedNodes:
if node in self.pecs.keys():
conductors.append((node, 'Conductor_{}'.format(len(conductors))))
if node in self.dielectrics.keys():
dielectrics.append((node, 'Dielectric_{}'.format(len(dielectrics))))
mappedElements.extend(conductors)

dielectrics = []
for node in self.dielectrics.keys():
dielectrics.append((node, 'Dielectric_{}'.format(len(dielectrics))))
mappedElements.extend(dielectrics)
return {element[0]:element[1] for element in mappedElements}

mappedComponents = {element[0]:element[1] for element in mappedElements}

for domain in self.vacuum.keys():
mappedComponents[domain] = domain
for openBoundary in self.open.keys():
mappedComponents[openBoundary] = 'OpenBoundary_0'

return mappedComponents
Loading