diff --git a/TODO.md b/TODO.md deleted file mode 100644 index fa1cae7..0000000 --- a/TODO.md +++ /dev/null @@ -1,56 +0,0 @@ -## Usefull utils -- createPatch -- polyDualMesh -- fvSolution: cellLimited -- collapseDict: collapseEdges -- renumberMesh -- processorField - -## Errors -- salome: - -th. 139990926538304 - -Trace /volatile/salome/jenkins/workspace/Salome_master_CO7/SALOME-9.6.0-CO7/SOURCES/SMESH/src/SMESH/SMESH_subMesh.cxx [2005] : -NETGEN_2D3D failed on sub-shape #1 with error COMPERR_BAD_INPUT_MESH -"NgException at Volume meshing: Stop meshing since surface mesh not consistent Some edges multiple times in surface mesh" - -th. 140588498282048 - -Trace /volatile/salome/jenkins/workspace/Salome_master_CO7/SALOME-9.6.0-CO7/SOURCES/SMESH/src/SMESH/SMESH_subMesh.cxx [2005] : -NETGEN_2D3D failed on sub-shape #47 with error COMPERR_WARNING -"Thickness 0.001 of viscous layers not reached, average reached thickness is 0.000928207" - -th. 139986338838080 - -Trace /volatile/salome/jenkins/workspace/Salome_master_CO7/SALOME-9.6.0-CO7/SOURCES/SMESH/src/SMESH/SMESH_subMesh.cxx [2005] : -NETGEN_2D3D failed on sub-shape #1 with error COMPERR_BAD_INPUT_MESH -"NgException at Volume meshing: Stop meshing since boundary mesh is overlapping Intersecting triangles" - - -## 1.03.21 -- [x] boundary type (wall or symetryPlane) -- [x] restruct for ways -- [x] build alpha = 0.01 .. 0.13 -- [x] ! symetryPlane -> cyclicAMI - -## 3.03.21 -- [x] configure salome server, ports, etc. -- [ ] less processes for salome, optimization. - -## 4.03.21 -- [x] 3rd direction -- [x] createPatch(Dict) -- [ ] views (mesh, ..) -- [x] alpha for simpleCubic [0.01 .. 0.28] -- [x] translation vector (cyclicAMI) -- [ ] BUG: angle between the direction vector and the normal to inlet is ~1.4e-14 - - [x] Another solution -- [ ] BUG: ideasUnvToFoam not working with param '-case PATH' - - [x] Temporary sulution via os.chdir(PATH) - -## 6.03.21 -- [ ] ERROR: MakeFuseList with alpha > 0.2 - -## 7.03.21 -- [x] Split the symetryPlane to 4 faces - -## 11.03.21 -- [x] Dual test for cyclicAMI diff --git a/anisotropy.sh b/anisotropy.sh new file mode 100755 index 0000000..da3a9be --- /dev/null +++ b/anisotropy.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +python ${DIR}/anisotropy/anisotropy.py diff --git a/__init__.py b/anisotropy/__init__.py similarity index 100% rename from __init__.py rename to anisotropy/__init__.py diff --git a/run.py b/anisotropy/anisotropy.py similarity index 92% rename from run.py rename to anisotropy/anisotropy.py index ce7116a..798f460 100644 --- a/run.py +++ b/anisotropy/anisotropy.py @@ -68,24 +68,6 @@ def main(): logger.info(f"Warnings: {logger.warnings}\tErrors: {logger.errors}") -def checkEnv(): - missed = False - - try: - pythonVersion = "Python {}".format(sys.version.split(" ")[0]) - salomeVersion = salome_utils.salomeVersion() - foamVersion = foam_utils.foamVersion() - - except Exception: - logger.critical("Missed environment") - missed = True - - else: - logger.info(f"environment:\n\t{pythonVersion}\n\t{salomeVersion}\n\t{foamVersion}") - - finally: - return missed - class Task: def __init__(self, **kwargs): @@ -223,4 +205,3 @@ def postprocessing(tasks): if __name__ == "__main__": main() - diff --git a/anisotropy/utils.py b/anisotropy/utils.py new file mode 100644 index 0000000..04920e4 --- /dev/null +++ b/anisotropy/utils.py @@ -0,0 +1,163 @@ +import logging + +from multiprocessing import Queue, Process, cpu_count +import socket + + +class struct: + def __init__(self, **kwargs): + for (k, v) in kwargs.items(): + setattr(self, k, v) + + def __str__(self): + members = [] + + for key in self.__dict__.keys(): + members.append(f"{ key } = ") + + if type(self.__dict__[key]) == str: + members[len(members) - 1] += f"\"{ self.__dict__[key] }\"" + + else: + members[len(members) - 1] += f"{ self.__dict__[key] }" + + return f"struct({', '.join(members)})" + + def __repr__(self): + return str(self) + + +class Logger: + def __init__(self, name, logpath): + logging.basicConfig( + level = logging.INFO, + format = "%(levelname)s: %(message)s", + handlers = [ + logging.StreamHandler(), + logging.FileHandler(logpath) + ] + ) + + self.logger = logging.getLogger(name) + self.warnings = 0 + self.errors = 0 + self.criticals = 0 + self.exceptions = 0 + + def info(self, *args): + self.logger.info(*args) + + def warning(self, *args): + self.warnings += 1 + self.logger.warning(*args) + + def error(self, *args): + self.errors += 1 + self.logger.error(*args) + + def critical(self, *args): + self.criticals += 1 + self.logger.critical(*args) + + def exception(self, *args): + self.exceptions += 1 + self.logger.exception(*args) + + def fancyline(self): + self.logger.info("-" * 80) + + + +def queue(cmd, qin, qout, *args): + + while True: + # Get item from the queue + pos, var = qin.get() + + # Exit point + if pos is None: + break + + # Execute command + res = cmd(*var, *args) + + # Put results to the queue + qout.put((pos, res)) + + return + + +def parallel(np, var, cmd): + + varcount = len(var) + + processes = [] + nprocs = np if np <= cpu_count() else cpu_count() + + qin = Queue(1) + qout = Queue() + + logging.info("cpu count: {}".format(np)) + logging.info("var: {}".format(var)) + logging.info("cmd: {}".format(cmd)) + + # Create processes + for n in range(nprocs): + pargs = [cmd, qin, qout] + + p = Process(target = queue, args = tuple(pargs)) + + processes.append(p) + + # Start processes + for p in processes: + p.daemon = True + p.start() + + # Fill queue + for n in range(varcount): + qin.put((n, var[n])) + + for _ in range(nprocs): + qin.put((None, None)) + + # Get results + results = [[] for n in range(varcount)] + + for n in range(varcount): + index, res = qout.get() + + results[index] = res + + # Wait until each processor has finished + for p in processes: + p.join() + + return results + + +def portIsFree(address, port): + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex((address, port)) == 0 + + +def checkEnv(): + missed = False + + try: + pythonVersion = "Python {}".format(sys.version.split(" ")[0]) + salomeVersion = salome_utils.salomeVersion() + foamVersion = foam_utils.foamVersion() + + except Exception: + logger.critical("Missed environment") + missed = True + + else: + logger.info(f"environment:\n\t{pythonVersion}\n\t{salomeVersion}\n\t{foamVersion}") + + finally: + return missed + + diff --git a/config.py b/config.py index c38645a..75a8993 100644 --- a/config.py +++ b/config.py @@ -163,4 +163,3 @@ class faceCentered: facesToIgnore = None, extrusionMethod = None ) - diff --git a/notes.md b/notes.md index c332ccb..32d0648 100644 --- a/notes.md +++ b/notes.md @@ -1,30 +1,8 @@ -## relaxationFactors -| p | Iterations | U | -| --- | --- | --- | -| 0.05 | 830 | 0.2 | -| 0.1 | 678 | 0.2 | -| 0.2 | 505 | 0.2 | -| 0.3 | 305 | 0.3 | -| 0.3 | 236 | 0.4 | -| 0.3 | 181 | 0.5 | +- createPatch +- polyDualMesh +- fvSolution: cellLimited +- collapseDict: collapseEdges +- renumberMesh +- processorField ---> - -## SIMPLE.residualControl - -| p | U | Iterations | -| --- | --- | --- | -| 1e-4 | 1e-4 | 338 | -| 1e-5 | 1e-5 | 499 | - -## - -```math -U = \frac{\Delta p L}{mu} \frac{9}{256} (\sqrt 2 - \frac{1}{1 - \alpha})^2 -L = 2 R_0 -R_0 = 1 -scale = 1e-5 -mu = 1e-3 -\Delta p = 1 -``` diff --git a/src/__init__.py b/openfoam/__init__.py similarity index 100% rename from src/__init__.py rename to openfoam/__init__.py diff --git a/openfoam/meshConversion.py b/openfoam/meshConversion.py new file mode 100644 index 0000000..83f410f --- /dev/null +++ b/openfoam/meshConversion.py @@ -0,0 +1,4 @@ + +def ideasUnvToFoam(mesh: str, case: str = None) -> (str, int): + return application("ideasUnvToFoam", mesh, case = case, stderr = True) + diff --git a/openfoam/meshManipulation.py b/openfoam/meshManipulation.py new file mode 100644 index 0000000..62f290e --- /dev/null +++ b/openfoam/meshManipulation.py @@ -0,0 +1,32 @@ + +def createPatch(dictfile: str = None, case: str = None): + args = ["-overwrite"] + + if dictfile: + args.extend(["-dict", dictfile]) + + application("createPatch", *args, case = case, stderr = True) + + +def transformPoints(scale: tuple, case: str = None): + scale_ = "{}".format(scale).replace(",", "") + + application("transformPoints", "-scale", scale_, case = case, stderr = True) + + +def checkMesh(case: str = None): + application("checkMesh", "-allGeometry", "-allTopology", case = case, stderr = True) + + with open("checkMesh.log", "r") as io: + warnings = [] + for line in io: + if re.search("\*\*\*", line): + warnings.append(line.replace("***", "").strip()) + + if warnings: + logger.warning("checkMesh:\n\t{}".format("\n\t".join(warnings))) + + +def renumberMesh(case: str = None): + application("renumberMesh", "-parallel", "-overwrite", useMPI = True, case = case, stderr = True) + diff --git a/openfoam/miscellaneous.py b/openfoam/miscellaneous.py new file mode 100644 index 0000000..c3f1ee3 --- /dev/null +++ b/openfoam/miscellaneous.py @@ -0,0 +1,9 @@ + +def foamDictionary(filepath: str, entry: str, value: str = None, case: str = None): + args = [filepath, "-entry", entry] + + if value: + args.extend(["-set", value]) + + application("foamDictionary", *args, case = case, stderr = False) + diff --git a/src/foam_utils.py b/openfoam/openfoam.py similarity index 99% rename from src/foam_utils.py rename to openfoam/openfoam.py index 9707c35..aba6714 100644 --- a/src/foam_utils.py +++ b/openfoam/openfoam.py @@ -123,4 +123,3 @@ def simpleFoam(case: str = None): return returncode - diff --git a/openfoam/parallelProcessing.py b/openfoam/parallelProcessing.py new file mode 100644 index 0000000..5be9153 --- /dev/null +++ b/openfoam/parallelProcessing.py @@ -0,0 +1,4 @@ + +def decomposePar(case: str = None): + application("decomposePar", case = case, stderr = True) + diff --git a/openfoam/solvers.py b/openfoam/solvers.py new file mode 100644 index 0000000..b1651d9 --- /dev/null +++ b/openfoam/solvers.py @@ -0,0 +1,15 @@ + +def potentialFoam(case: str = None): + application("potentialFoam", "-parallel", useMPI = True, case = case, stderr = True) + + +def simpleFoam(case: str = None): + _, returncode = application("simpleFoam", "-parallel", useMPI = True, case = case, stderr = True) + + with open("simpleFoam.log", "r") as io: + for line in io: + if re.search("solution converged", line): + logger.info("simpleFoam:\n\t{}".format(line.strip())) + + return returncode + diff --git a/src/cubicFoam/0/U b/openfoam/template/0/U similarity index 100% rename from src/cubicFoam/0/U rename to openfoam/template/0/U diff --git a/src/cubicFoam/0/p b/openfoam/template/0/p similarity index 100% rename from src/cubicFoam/0/p rename to openfoam/template/0/p diff --git a/src/cubicFoam/constant/transportProperties b/openfoam/template/constant/transportProperties similarity index 100% rename from src/cubicFoam/constant/transportProperties rename to openfoam/template/constant/transportProperties diff --git a/src/cubicFoam/constant/turbulenceProperties b/openfoam/template/constant/turbulenceProperties similarity index 100% rename from src/cubicFoam/constant/turbulenceProperties rename to openfoam/template/constant/turbulenceProperties diff --git a/src/cubicFoam/system/collapseDict b/openfoam/template/system/collapseDict similarity index 100% rename from src/cubicFoam/system/collapseDict rename to openfoam/template/system/collapseDict diff --git a/src/cubicFoam/system/controlDict b/openfoam/template/system/controlDict similarity index 100% rename from src/cubicFoam/system/controlDict rename to openfoam/template/system/controlDict diff --git a/src/cubicFoam/system/createPatchDict.cyclic b/openfoam/template/system/createPatchDict.cyclic similarity index 100% rename from src/cubicFoam/system/createPatchDict.cyclic rename to openfoam/template/system/createPatchDict.cyclic diff --git a/src/cubicFoam/system/createPatchDict.symetry b/openfoam/template/system/createPatchDict.symetry similarity index 100% rename from src/cubicFoam/system/createPatchDict.symetry rename to openfoam/template/system/createPatchDict.symetry diff --git a/src/cubicFoam/system/decomposeParDict b/openfoam/template/system/decomposeParDict similarity index 100% rename from src/cubicFoam/system/decomposeParDict rename to openfoam/template/system/decomposeParDict diff --git a/src/cubicFoam/system/fvSchemes b/openfoam/template/system/fvSchemes similarity index 100% rename from src/cubicFoam/system/fvSchemes rename to openfoam/template/system/fvSchemes diff --git a/src/cubicFoam/system/fvSolution b/openfoam/template/system/fvSolution similarity index 100% rename from src/cubicFoam/system/fvSolution rename to openfoam/template/system/fvSolution diff --git a/openfoam/utils.py b/openfoam/utils.py new file mode 100644 index 0000000..8359a57 --- /dev/null +++ b/openfoam/utils.py @@ -0,0 +1,14 @@ + +def foamVersion() -> str: + return "OpenFOAM-{}".format(os.environ["WM_PROJECT_VERSION"]) + + +def foamClean(case: str = None): + rmDirs = ["0", "constant", "system", "postProcessing", "logs"] + rmDirs.extend([ "processor{}".format(n) for n in range(os.cpu_count()) ]) + path = case if case else "" + + for d in rmDirs: + if os.path.exists(os.path.join(path, d)): + shutil.rmtree(os.path.join(path, d)) + diff --git a/postProcessing/__init__.py b/postProcessing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/paraview_utils.py b/postProcessing/utils.py similarity index 100% rename from src/paraview_utils.py rename to postProcessing/utils.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/salome/__init__.py b/salome/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/samples/bodyCentered.py b/salome/bodyCentered.py similarity index 99% rename from samples/bodyCentered.py rename to salome/bodyCentered.py index 0f33c91..d5a37cc 100644 --- a/samples/bodyCentered.py +++ b/salome/bodyCentered.py @@ -1,5 +1,5 @@ -import salome -salome.salome_init() +#import salome +#salome.salome_init() import GEOM from salome.geom import geomBuilder @@ -284,4 +284,3 @@ def bodyCenteredHexagonalPrism(theta = 0.01, fillet = False, direction = [1, 1, groups.append(wall) return shape, groups - diff --git a/samples/faceCentered.py b/salome/faceCentered.py similarity index 99% rename from samples/faceCentered.py rename to salome/faceCentered.py index 0a24740..cbbb4cb 100644 --- a/samples/faceCentered.py +++ b/salome/faceCentered.py @@ -1,5 +1,5 @@ -import salome -salome.salome_init() +#import salome +#salome.salome_init() import GEOM from salome.geom import geomBuilder @@ -281,4 +281,3 @@ def faceCenteredHexagonalPrism(theta = 0.01, fillet = False, direction = [1, 1, groups.append(wall) return shape, groups - diff --git a/samples/__init__.py b/salome/genmesh.py similarity index 98% rename from samples/__init__.py rename to salome/genmesh.py index 8e81dd8..940d74e 100644 --- a/samples/__init__.py +++ b/salome/genmesh.py @@ -1,3 +1,6 @@ +### +# This file executes inside salome environment +## from collections import namedtuple import os, sys import logging diff --git a/src/geometry_utils.py b/salome/geometry.py similarity index 99% rename from src/geometry_utils.py rename to salome/geometry.py index 5486ada..b25b199 100644 --- a/src/geometry_utils.py +++ b/salome/geometry.py @@ -236,4 +236,3 @@ def boundaryCreate(gobj, dvec, grains): return boundary - diff --git a/src/mesh_utils.py b/salome/mesh.py similarity index 99% rename from src/mesh_utils.py rename to salome/mesh.py index 925378b..a0ca20d 100644 --- a/src/mesh_utils.py +++ b/salome/mesh.py @@ -189,4 +189,3 @@ def meshExport(mobj, path): except: logger.error("""meshExport: Cannot export.""") - diff --git a/samples/simple.py b/salome/simple.py similarity index 99% rename from samples/simple.py rename to salome/simple.py index a005307..cdba00e 100644 --- a/samples/simple.py +++ b/salome/simple.py @@ -1,5 +1,5 @@ -import salome -salome.salome_init() +#import salome +#salome.salome_init() import GEOM from salome.geom import geomBuilder @@ -261,4 +261,3 @@ def simpleHexagonalPrism(theta = 0.01, fillet = False, direction = [1, 1, 1]): groups.append(wall) return shape, groups - diff --git a/src/salome_utils.py b/salome/utils.py similarity index 99% rename from src/salome_utils.py rename to salome/utils.py index 7ff6453..b891a23 100644 --- a/src/salome_utils.py +++ b/salome/utils.py @@ -80,4 +80,3 @@ def remote(port, cmd): stderr = subprocess.PIPE) return p - diff --git a/src/anisotropeCubic.py b/src/anisotropeCubic.py deleted file mode 100644 index bf68368..0000000 --- a/src/anisotropeCubic.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import math -from . import geometry_utils - -import GEOM -geompy = geometry_utils.getGeom() - -class StructuredGrains: - def __init__(self, radius, stackAngle, theta, layers): - self.pos = [0, 0, 0] - self.angle = [0, 0, 0] - self.radius = radius - self.theta = theta - self.layers = layers - - # Parameters and dependencies - R = self.radius / (1 - self.theta) - - C1 = 0.8 #fillet[0] - C2 = 0.4 #fillet[1] - self.theta1 = 0.01 - self.theta2 = 0.28 - - Cf = C1 + (C2 - C1) / (self.theta2 - self.theta1) * (self.theta - self.theta1) - R_fillet = Cf * (self.radius * math.sqrt(2) - R) - - ### - stackang = [ - 0.5 * math.pi - stackAngle[0], - 0.5 * math.pi - stackAngle[1], - 0.5 * math.pi - stackAngle[2] - ] - - xvec = geompy.MakeVector( - geompy.MakeVertex(0, 0, 0), - geompy.MakeVertex(1, 0, 0)) - yvec = geometry_utils.rotate(xvec, [0.5 * math.pi, 0, 0]) - zvec = geometry_utils.rotate(xvec, [0, 0.5 * math.pi, 0]) - - grain = geompy.MakeSpherePntR(geompy.MakeVertex(pos[0], pos[1], pos[2]), R) - - xstack = geompy.MakeMultiTranslation1D(grain, xvec, 2 * self.radius, self.layers[0]) - ystack = geompy.MakeMultiTranslation1D(xgrain, yvec, 2 * self.radius, self.layers[1]) - zstack = geompy.MakeMultiTranslation1D(ygrain, zvec, 2 * self.radius, self.layers[2]) - - # Correct position to zero - stack = geompy.MakeTranslation(zstack, -2 * self.radius, 0, 0) - - self.geometry = geompy.ExtractShapes(stack, geompy.ShapeType["SOLID"], True) - self.geometry = geompy.MakeFuseList(self.geometry, False, False) - - if not R_fillet == 0: - self.geometry = geompy.MakeFilletAll(self.geometry, R_fillet) - - -class AnisotropeCubic: - def __init__(self, scale, grains, style): - self.pos = [0, 0, 0] - self.angle = [0, 0, 0] - self.scale = scale - self.grains = grains - - # Bounding box - if style == 0: - # Square - profile = ( - geompy.Sketcher3D() - .addPointAbsolute(0, 0, 0) - .addPointAbsolute(0, 0, self.scale[2]) - .addPointAbsolute(0, self.scale[1], self.scale[2]) - .addPointAbsolute(0, self.scale[1], 0) - .addPointAbsolute(0, 0, 0) - ) - - face = geompy.MakeFaceWires([profile.wire()], 1) - - elif style == 1: - # Rombus - profile = ( - geompy.Sketcher3D() - .addPointAbsolute(self.scale[0], 0.5 * self.scale[1], 0) - .addPointAbsolute(0.5 * self.scale[0], 0, 0.5 * self.scale[2]) - .addPointAbsolute(0, 0.5 * self.scale[1], self.scale[2]) - .addPointAbsolute(0.5 * self.scale[0], self.scale[1], 0.5 * self.scale[2]) - .addPointAbsolute(self.scale[0], 0.5 * self.scale[1], 0) - ) - - face = geompy.MakeFaceWires([profile.wire()], 1) - face = geompy.MakeTranslation(face, - 0.5 * self.scale[1], 0, 0) - - self.boundingbox = geompy.MakePrismVecH(face, - geompy.MakeVectorDXDYDZ(1, 0, 0), - self.scale[0]) - - # Geometry - self.geometry = geompy.MakeCutList(box, [self.grains], True) - - - - diff --git a/src/applogger.py b/src/applogger.py deleted file mode 100644 index 30ac1c3..0000000 --- a/src/applogger.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging -import config - -class Logger(): - def __init__(self): - logging.basicConfig( - level = logging.INFO, - format = "%(levelname)s: %(message)s", - handlers = [ - logging.StreamHandler(), - logging.FileHandler(f"{config.LOG}/anisotrope.log") - ] - ) - - self.logger = logging.getLogger("anisotrope") - self.warnings = 0 - self.errors = 0 - self.criticals = 0 - self.exceptions = 0 - - def info(self, *args): - self.logger.info(*args) - - def warning(self, *args): - self.warnings += 1 - self.logger.warning(*args) - - def error(self, *args): - self.errors += 1 - self.logger.error(*args) - - def critical(self, *args): - self.criticals += 1 - self.logger.critical(*args) - - def exception(self, *args): - self.exceptions += 1 - self.logger.exception(*args) - - def fancyline(self): - self.logger.info("-" * 80) diff --git a/src/utils.py b/src/utils.py deleted file mode 100644 index 8392182..0000000 --- a/src/utils.py +++ /dev/null @@ -1,77 +0,0 @@ -from multiprocessing import Queue, Process, cpu_count -import socket -import logging - -def queue(cmd, qin, qout, *args): - - while True: - # Get item from the queue - pos, var = qin.get() - - # Exit point - if pos is None: - break - - # Execute command - res = cmd(*var, *args) - - # Put results to the queue - qout.put((pos, res)) - - return - - -def parallel(np, var, cmd): - - varcount = len(var) - - processes = [] - nprocs = np if np <= cpu_count() else cpu_count() - - qin = Queue(1) - qout = Queue() - - logging.info("cpu count: {}".format(np)) - logging.info("var: {}".format(var)) - logging.info("cmd: {}".format(cmd)) - - # Create processes - for n in range(nprocs): - pargs = [cmd, qin, qout] - - p = Process(target = queue, args = tuple(pargs)) - - processes.append(p) - - # Start processes - for p in processes: - p.daemon = True - p.start() - - # Fill queue - for n in range(varcount): - qin.put((n, var[n])) - - for _ in range(nprocs): - qin.put((None, None)) - - # Get results - results = [[] for n in range(varcount)] - - for n in range(varcount): - index, res = qout.get() - - results[index] = res - - # Wait until each processor has finished - for p in processes: - p.join() - - return results - - -def portIsFree(address, port): - - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return s.connect_ex((address, port)) == 0 -