diff --git a/docs/developer_guide/measurements.md b/docs/developer_guide/measurements.md index f7aac5af4..130c38f80 100644 --- a/docs/developer_guide/measurements.md +++ b/docs/developer_guide/measurements.md @@ -13,7 +13,6 @@ hera/measurements/ topography.py # TopographyToolkit — SRTM elevation data landcover.py # LandCoverToolkit — MODIS land cover tiles.py # TilesToolkit — tile server map images - hill2stl.py # STL mesh generation from elevation vector/ toolkit.py # VectorToolkit — base class for vector GIS topography.py # TopographyToolkit (vector contours) @@ -79,12 +78,6 @@ Key constants and functions used across GIS toolkits: | `ITM` | Israeli Transverse Mercator CRS identifier (EPSG:2039) | | `convertCRS(points, inputCRS, outputCRS)` | Transform coordinates between CRS | -### STL generation (`GIS/raster/hill2stl.py`) - -The `hill2stl` module converts elevation grids to STL meshes for CFD simulations. Used by both the raster topography and buildings toolkits. - ---- - ## Meteorology toolkits ### lowFreqToolKit diff --git a/docs/developer_guide/measurements/gis.md b/docs/developer_guide/measurements/gis.md index 9c915de9b..e88048c24 100644 --- a/docs/developer_guide/measurements/gis.md +++ b/docs/developer_guide/measurements/gis.md @@ -17,7 +17,6 @@ hera/measurements/GIS/ topography.py # TopographyToolkit — SRTM elevation landcover.py # LandCoverToolkit — MODIS land cover + roughness tiles.py # TilesToolkit — tile server map images - hill2stl.py # STL mesh generation from elevation grids vector/ toolkit.py # VectorToolkit — base class for vector GIS topography.py # TopographyToolkit — contour lines diff --git a/docs/developer_guide/measurements/index.md b/docs/developer_guide/measurements/index.md index 4035fab38..6df5530eb 100644 --- a/docs/developer_guide/measurements/index.md +++ b/docs/developer_guide/measurements/index.md @@ -13,7 +13,6 @@ hera/measurements/ topography.py # TopographyToolkit — SRTM elevation data landcover.py # LandCoverToolkit — MODIS land cover tiles.py # TilesToolkit — tile server map images - hill2stl.py # STL mesh generation from elevation vector/ toolkit.py # VectorToolkit — base class for vector GIS topography.py # TopographyToolkit (vector contours) diff --git a/docs/developer_guide/simulations.md b/docs/developer_guide/simulations.md index 0feb1fa16..2ad63adbd 100644 --- a/docs/developer_guide/simulations.md +++ b/docs/developer_guide/simulations.md @@ -21,7 +21,6 @@ hera/simulations/ preprocessOFObjects/ # Mesh and case preprocessing utilities postProcess/ VTKPipeline.py # VTK post-processing pipeline - VTKPipelineExecutionContext.py LSM/ toolkit.py # LSMToolkit — Lagrangian Stochastic Model singleSimulation.py # Single LSM simulation handler @@ -248,7 +247,6 @@ The VTK pipeline provides a ParaView-integrated post-processing system with DB c | `VTKPipeLine` | Pipeline container — creates/manages filters, exports to JSON | | `registeredVTKPipeLine` | Pipeline bound to a specific case — executes filters, caches results | | `VTKFilter` | Base filter node — tree structure with `downstream` children | -| `VTKPipelineExecutionContext` | Reader and pipeline configuration for paraview execution | | `paraviewOpenFOAM` (`pvOpenFOAMBase.py`) | ParaView backend — reads OpenFOAM cases, writes results | **Available filters:** diff --git a/docs/developer_guide/simulations/index.md b/docs/developer_guide/simulations/index.md index 84de61b28..dfd678ee6 100644 --- a/docs/developer_guide/simulations/index.md +++ b/docs/developer_guide/simulations/index.md @@ -21,7 +21,6 @@ hera/simulations/ preprocessOFObjects/ # Mesh and case preprocessing utilities postProcess/ VTKPipeline.py # VTK post-processing pipeline - VTKPipelineExecutionContext.py LSM/ toolkit.py # LSMToolkit — Lagrangian Stochastic Model singleSimulation.py # Single LSM simulation handler diff --git a/docs/developer_guide/simulations/openfoam.md b/docs/developer_guide/simulations/openfoam.md index 735010a21..59255ae09 100644 --- a/docs/developer_guide/simulations/openfoam.md +++ b/docs/developer_guide/simulations/openfoam.md @@ -258,7 +258,6 @@ The VTK pipeline provides a ParaView-integrated post-processing system with DB c | `VTKPipeLine` | Pipeline container — creates/manages filters, exports to JSON | | `registeredVTKPipeLine` | Pipeline bound to a specific case — executes filters, caches results | | `VTKFilter` | Base filter node — tree structure with `downstream` children | -| `VTKPipelineExecutionContext` | Reader and pipeline configuration for paraview execution | | `paraviewOpenFOAM` (`pvOpenFOAMBase.py`) | ParaView backend — reads OpenFOAM cases, writes results | **Available filters:** diff --git a/hera/measurements/GIS/raster/hill2stl.py b/hera/measurements/GIS/raster/hill2stl.py deleted file mode 100644 index 5ab64c1cb..000000000 --- a/hera/measurements/GIS/raster/hill2stl.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Feb 18 10:14:22 2025 - -@author: nirb - -We create a STL file from a mathematical function. -We might want to change the code to allow it to use other parts of the repository e.g. create STL in topography -""" -import numpy as np - -def function(x, y): - """Define your function here.""" - return np.cos(x)**2 * np.cos(y)**2 + 3 - -def generate_solid_stl(f, x_range=(-20, 20), y_range=(-12, 12), resolution=100, filename="solid_output.stl"): - """Generates a solid STL file with a flat bottom.""" - x = np.linspace(x_range[0], x_range[1], resolution) - y = np.linspace(y_range[0], y_range[1], resolution) - X, Y = np.meshgrid(x, y) - Z = f(X, Y) # Compute function values - - z_min = np.min(Z) - 0.5 # Set a flat base slightly below the surface - - # Open file for writing ASCII STL - with open(filename, "w") as stl_file: - stl_file.write("solid solid_surface\n") - - # Create top surface - for i in range(resolution - 1): - for j in range(resolution - 1): - v1 = (X[i, j], Y[i, j], Z[i, j]) - v2 = (X[i, j+1], Y[i, j+1], Z[i, j+1]) - v3 = (X[i+1, j], Y[i+1, j], Z[i+1, j]) - v4 = (X[i+1, j+1], Y[i+1, j+1], Z[i+1, j+1]) - - write_triangle(stl_file, v1, v2, v3) - write_triangle(stl_file, v2, v4, v3) - - # Create flat base - for i in range(resolution - 1): - for j in range(resolution - 1): - v1 = (X[i, j], Y[i, j], z_min) - v2 = (X[i, j+1], Y[i, j+1], z_min) - v3 = (X[i+1, j], Y[i+1, j], z_min) - v4 = (X[i+1, j+1], Y[i+1, j+1], z_min) - - write_triangle(stl_file, v1, v3, v2) - write_triangle(stl_file, v2, v3, v4) - - # Create vertical side walls - for i in range(resolution - 1): - for j in [0, resolution - 1]: # Front and back - v1 = (X[i, j], Y[i, j], Z[i, j]) - v2 = (X[i+1, j], Y[i+1, j], Z[i+1, j]) - v3 = (X[i, j], Y[i, j], z_min) - v4 = (X[i+1, j], Y[i+1, j], z_min) - - write_triangle(stl_file, v1, v2, v3) - write_triangle(stl_file, v2, v4, v3) - - for j in range(resolution - 1): - for i in [0, resolution - 1]: # Left and right - v1 = (X[i, j], Y[i, j], Z[i, j]) - v2 = (X[i, j+1], Y[i, j+1], Z[i, j+1]) - v3 = (X[i, j], Y[i, j], z_min) - v4 = (X[i, j+1], Y[i, j+1], z_min) - - write_triangle(stl_file, v1, v2, v3) - write_triangle(stl_file, v2, v4, v3) - - stl_file.write("endsolid solid_surface\n") - - print(f"STL file saved as {filename}") - -def write_triangle(file, v1, v2, v3): - """Writes a triangle to the STL file in ASCII format.""" - normal = compute_normal(v1, v2, v3) - file.write(f" facet normal {normal[0]:.6f} {normal[1]:.6f} {normal[2]:.6f}\n") - file.write(" outer loop\n") - file.write(f" vertex {v1[0]:.6f} {v1[1]:.6f} {v1[2]:.6f}\n") - file.write(f" vertex {v2[0]:.6f} {v2[1]:.6f} {v2[2]:.6f}\n") - file.write(f" vertex {v3[0]:.6f} {v3[1]:.6f} {v3[2]:.6f}\n") - file.write(" endloop\n") - file.write(" endfacet\n") - -def compute_normal(v1, v2, v3): - """Computes the normal of a triangle given three vertices.""" - v1, v2, v3 = np.array(v1), np.array(v2), np.array(v3) - normal = np.cross(v2 - v1, v3 - v1) - normal = normal / np.linalg.norm(normal) # Normalize - return normal - -# Run the function -minx=-2 -maxx=2 -miny=-3 -maxy=3 -filename='test1.stl' -generate_solid_stl(function, x_range=(minx, maxx), y_range=(miny, maxy), resolution=100, filename=filename) diff --git a/hera/simulations/LSM/hermesWorkflowToolkit.py b/hera/simulations/LSM/hermesWorkflowToolkit.py deleted file mode 100644 index 2cd1c3183..000000000 --- a/hera/simulations/LSM/hermesWorkflowToolkit.py +++ /dev/null @@ -1,785 +0,0 @@ -from enum import Enum, auto, unique -from typing import Union -import pandas -import shutil -import shlex -import subprocess -import os -from hera.toolkit import abstractToolkit -from hera.utils import loadJSON, compareJSONS -from hera.utils.query import dictToMongoQuery -from hera.utils.dataframeutils import compareDataframeConfigurations -from hera.datalayer import datatypes -import numpy -import pydoc -import uuid -import warnings -from hera.utils.logging import with_logger, get_classMethod_logger -from hera.simulations.hermesWorkflowToolkit import buildLuigiExecutionCommand, SCHEDULER_LOCAL, SCHEDULER_CENTRAL - -try: - from hermes import workflow - from hermes.utils.workflowAssembly import handler_build,handler_buildExecute,handler_expand,handler_execute -except ImportError: -# raise ImportError("hermes is not installed. please install it to use the hermes workflow toolkit.") - warnings.warn("hermes is not installed. some features will not work.") - workflow = None - - -@unique -class actionModes(Enum): - """Workflow action modes controlling which steps are executed. - - - ``ADD`` — add the workflow document to the database only. - - ``ADDBUILD`` — add and build (generate LSM input files). - - ``ADDBUILDEXECUTE`` — add, build, and execute the LSM simulation. - """ - ADD = auto() - ADDBUILD = auto() - ADDBUILDEXECUTE = auto() - - -class workflowToolkit(abstractToolkit): - """ - Manages the hermes worflows: - - 1. Checks if they are in the DB. - 2. create a new name to them - 3. allows simple deletion - 4. allows simple comparison. - 5. retrieve by initial name. - """ - DESC_GROUPNAME = "groupName" - DESC_GROUPID = "groupID" - DESC_WORKFLOWNAME = "workflowName" - DESC_PARAMETERS = "parameters" - - DOCTYPE_WORKFLOW = "hermesWorkflow" - - def __init__(self, projectName: str, filesDirectory: str = None,toolkitName : str="hermesWorkflowToolkit", connectionName=None): - """ - Initializes the workflow toolkit. - - Parameters - ---------- - projectName: str - The project that the workflow will be used i. - - filesDirectory : str - The directory to write all the Workflow and the outputs. default is current directory. - """ - super().__init__(projectName=projectName, - filesDirectory=filesDirectory, - toolkitName =toolkitName, - connectionName=connectionName) - - # ## Create the simulationType->object map - # self._simulationTypeMap = { - # WorkflowTypes.WORKFLOW.value : "hermes.workflow", - # WorkflowTypes.OF_DISPERSION.value : "hera.simulations.openFoam.datalayer.hermesWorkflow.Workflow_Dispersion", - # WorkflowTypes.OF_FLOWFIELD.value : "hera.simulations.openFoam.datalayer.hermesWorkflow.Workflow_Flow" - # } - - - def getHemresWorkflowFromDocument(self,documentList,returnFirst=True): - """ - Return a hermes-workflow (or a list of hermes Workflow) to the user. - - Parameters - ---------- - documentList : list, document - A hera.datalayer document or a list of documents. - - returnFirst : bool - If true, return obj of only the first iterm in the list (if it is a list). - - Returns - ------- - hermes workflow object (or one of its derivatives). - """ - - docList = numpy.atleast_1d(documentList) - - if returnFirst: - doc = docList[0] - ret = self.getHermesWorkflowFromJSON(doc.desc['workflow'],name=doc.desc['workflowName']) - else: - ret = [self.getHermesWorkflowFromJSON(doc.desc['workflow'],name=doc.desc['workflowName']) for doc in docList] - - return ret - - - def getHermesWorkflowFromJSON(self,workflow : Union[dict,str],name=None): - """ - Creates a hermes workflow object from the JSON that is supplied. - - The JSON can be either file name, JSON string or a dictionary. - - Parameters - ---------- - workflow: dict,str - The - - simulationType : str - The type of the workflow to create. - - Returns - ------- - hermesWorkflow object. - """ - workFlowJSON = loadJSON(workflow) - ky = workFlowJSON['workflow'].get('solver',None) - - if ky is None: - hermesWFObj = pydoc.locate("hermes.workflow") - else: - hermesWFObj = pydoc.locate(f"hera.simulations.openFoam.OFWorkflow.workflow_{ky}") - - if hermesWFObj is None: - err = f"The workflow type {ky} not found" - self.logger.error(err) - raise ValueError(err) - - return hermesWFObj(workFlowJSON,name=name) - - - def getHermesWorkflowFromDB(self,nameOrWorkflowFileOrJSONOrResource : Union[dict, str,list,workflow],returnFirst=True,**query): - """ - Retrieve Workflow from the DB as hermes.workflow objects (or its derivatives). - - If the workflow is string, use it as a name. If the workflow is dict, - use it as a filter on the paramters - - If returnFirst is False, return a list with all the results of the query. - else, returns a single hermesworkflow. - - Parameters - ---------- - workflow: str, dict - The filtering criteria. Either name, or the parameters of the flow. - - returnFirst : bool - If true, return only the first object (if found several results in the DB) - - query: arguments - Additional query criteria. - - Returns - ------- - list (returnFirst is False) - hermes workflow. - """ - - docList = self.getWorkflowListDocumentFromDB(nameOrWorkflowFileOrJSONOrResource, **query) - - if len(docList) == 0: - self.logger.error(f"... not found. ") - ret = None - else: - ret = self.getHemresWorkflowFromDocument(documentList=docList,returnFirst=returnFirst) - return ret - - - - def getWorkflowDocumentFromDB(self, nameOrWorkflowFileOrJSONOrResource, doctype=None, dockind="Simulations", **query): - """ - Tries to find item as name, workflow directory , groupname or through the resource. - Additional queries are also applicable. - - Parameters - ---------- - nameOrWorkflowFileOrJSONOrResource : string or dict - The name/dict that defines the item - doctype : string - document type. - - dockind : string - Whether the document is cachaed or Simulation. - - query : dict - - Additional criteria. - Returns - ------- - doc or empty list if not found. - """ - doctype = self.DOCTYPE_WORKFLOW if doctype is None else doctype - mongo_crit = dictToMongoQuery(query) - - # Dynamic dispatch: retrieve from Simulations or Cache collection. - retrieve_func = getattr(self,f"get{dockind}Documents") - - if isinstance(nameOrWorkflowFileOrJSONOrResource, str): - # Cascading search for string inputs (same strategy as main toolkit): - # 1) workflowName → 2) resource path → 3) groupName → 4) JSON content - self.logger.debug(f"Searching for {nameOrWorkflowFileOrJSONOrResource} as a name.") - docList = retrieve_func(workflowName=nameOrWorkflowFileOrJSONOrResource, type=doctype,**mongo_crit) - if len(docList) == 0: - self.logger.debug(f"Searching for {nameOrWorkflowFileOrJSONOrResource} as a resource.") - docList = retrieve_func(resource=nameOrWorkflowFileOrJSONOrResource, type=doctype,**mongo_crit) - if len(docList) == 0: - self.logger.debug(f"Searching for {nameOrWorkflowFileOrJSONOrResource} as a workflow group.") - docList = retrieve_func(groupName=nameOrWorkflowFileOrJSONOrResource,type=doctype,**mongo_crit) - if len(docList) == 0: - # Last resort: parse as JSON and query by parameter values. - self.logger.debug(f"... not found. Try to query as a json. ") - try: - jsn = loadJSON(nameOrWorkflowFileOrJSONOrResource) - wf = self.getHermesWorkflowFromJSON(jsn) - currentQuery = dictToMongoQuery(wf.parametersJSON, prefix="parameters") - currentQuery.update(mongo_crit) - docList = retrieve_func(type=self.DOCTYPE_WORKFLOW, **currentQuery) - except ValueError: - - # self.logger.debug(f"Searching for {nameOrWorkflowFileOrJSONOrResource} as a file.") - # if os.path.isfile(nameOrWorkflowFileOrJSONOrResource): - # from ..datalayer.document import nonDBMetadataFrame - # workflowName = os.path.basename(nameOrWorkflowFileOrJSONOrResource).split(".")[0] - # grpTuple = workflowName.split("_") - # groupName = grpTuple[0] - # groupID = grpTuple[1] if len(grpTuple) > 1 else 0 - # hermesWF = self.getHermesWorkflowFromJSON(nameOrWorkflowFileOrJSONOrResource) - # res = nonDBMetadataFrame(data=None, - # projecName=self.projectName, - # resource=os.path.join(self.FilesDirectory, - # nameOrWorkflowFileOrJSONOrResource), - # dataFormat=datatypes.STRING, - # type=self.DOCTYPE_WORKFLOW, - # groupName=groupName, - # groupID=groupID, - # workflowName=workflowName, - # workflowType=hermesWF.workflowType, - # workflow=hermesWF.json, - # parameters=hermesWF.parametersJSON - # ) - # docList = [res] - # else: - self.logger.debug(f"not found") - docList = [] - except IsADirectoryError: - self.logger.debug(f"not found") - docList = [] - else: - self.logger.info(f"... Found it as workflow group ") - else: - self.logger.info(f"... Found it as resource ") - else: - self.logger.info(f"... Found it as name") - - elif isinstance(nameOrWorkflowFileOrJSONOrResource, dict) or isinstance(nameOrWorkflowFileOrJSONOrResource, workflow): - qryDict = nameOrWorkflowFileOrJSONOrResource.parametersJSON if isinstance(nameOrWorkflowFileOrJSONOrResource, workflow) else nameOrWorkflowFileOrJSONOrResource - self.logger.debug(f"Searching for {qryDict} using parameters") - currentQuery = dictToMongoQuery(qryDict, prefix="parameters") - currentQuery.update(mongo_crit) - docList = retrieve_func(**currentQuery, type=self.DOCTYPE_WORKFLOW) - else: - docList = [] - - return docList - - - def getWorkflowListDocumentFromDB(self, nameOrWorkflowFileOrJSONOrResource : Union[dict, str, list, workflow], **query): - """ - Returns the simulation document from the DB. - The nameOrWorkflowFileOrJSONOrResource can be either group name - - Identify the simulation from : - - Resource (thedirectory name) - - Simulation name - - Its workflow - - workfolow dict. - - Return the first item that was found. - - Parameters - ---------- - nameOrWorkflowFileOrJSONOrResource: str, dict - - Can be - - Resource (thedirectory name) - - Simulation name - - Its workflow - - workfolow dict. - - query : dict - Additional query cireteria to the DB. - - Returns - ------- - A document, or None if not found. . - """ - - if isinstance(nameOrWorkflowFileOrJSONOrResource,list): - docList = [] - for simulationItem in nameOrWorkflowFileOrJSONOrResource: - docList += self.getWorkflowDocumentFromDB(simulationItem) - else: - docList = self.getWorkflowDocumentFromDB(nameOrWorkflowFileOrJSONOrResource,**query) - - return docList - - def getWorkflowInGroup(self, groupName: str, **kwargs): - """ - Return a list of all the simulations.old with the name as a prefic, and of the requested simuationType. - Returns the list of the documents. - - If the simuationType is None use the default simuationType (WORKFLOW). - - Parameters - ---------- - - groupName : str - The prefix name of all the runs. - - simulationType : str [optional] - The type of the workflow. - if None, return all. - - kwargs: additional filtering criteria. - Use mongodb criteria. - - Returns - ------- - list of mongo documents. - - """ - return self.getSimulationsDocuments(groupName=groupName, type=self.DOCTYPE_WORKFLOW, **kwargs) - - def findAvailableName(self, simulationGroup: str, **kwargs): - """ - Finds the next availabe name of that prefix. The available name is the maximal ID + 1. - - we assume that the name of each run is: - _. - - - Parameters - ---------- - simulationGroup : str - The simulation group - - simulationType : str - The type of the workflow. - - **kwargs : dict - additional fileters. - Note that these should be the full path of the parameter in the JSON. - - Returns - ------- - int,str - The new ID, - The name. - """ - simList = self.getWorkflowInGroup(groupName=simulationGroup, **kwargs) - group_ids = [int(x['desc']['groupID']) for x in simList if x['desc']['groupID'] is not None] - if len(group_ids) == 0: - newID = 1 - else: - newID = int(numpy.max(group_ids)+1) - - return newID, self.getworkFlowName(simulationGroup,newID) - - @staticmethod - def getworkFlowName(baseName,flowID): - """ - Returns the name of the flow field from the base and the - flow id. - - The name is _ - where is padded. - - Parameters - ---------- - baseName : str - The base name - flowID: int - the id of the name. - - Returns - ------- - - """ - formatted_number = "{0:04d}".format(flowID) - return f"{baseName}_{formatted_number}" - - - def addWorkflowToGroup(self, - workflowJSON: str, - groupName: str = None, - overwrite: bool = False, - force: bool = False, - assignName: bool = False, - execute: bool = False, - parameters: dict = dict(), - scheduler: str = SCHEDULER_LOCAL, - schedulerHost: str = None, - schedulerPort: int = None, - dispatch_id: str = None): - """ - 1. Adds the workflow to the database in the requested group - 2. Builds the template (.json) and python executer - 3. Runs the workflow. - - The stages are executed according to the buildMode. - - Notes: - - * If the workflow is already in the db in a different name adds to the db only if **force** is True. - - * If the workflowName already exist in the group then overwrite its definitions - only if the **overwrite** is True. - - * If the template and python execution files exist on the disk, raise error unless overwrite is True. - - * If the group is None, parse the file name to get the group. That is, we assume that the - file name has the structure : _. If the is not an integer, - the id in the database will be saved as None. - - Parameters - ---------- - workflowJSON : str - The file name that contains the workflow. - - groupName : str - The group to assign the workflow to. - If None, parse the name under the format - _ to get the group and the ID. - - overwrite : bool - If true, update the record if it exists. - If false, throw an exception if the record exists. - - force : bool - Allow duplicate Workflow in the project. - - assignName : bool - If true, finds the next available id and saves it in the DB. - If groupName is None, parse the filename to get the group. - - Note that if true and group name is None, the names of the simulation will use only the string before the '_'. - - Otherwise, use the filename as the name of the simulation. - - execute : bool - If true, execute the workflow - - buildModes enum. - ADDBUILDEXECUTE : add the simulation to the db, builds the execution files (also saves the new tempalte) and executes it. - if the file already in the db -> overwrite if over - ADDBUILD : add to the db and build the template and execution files. - ADD : add to the db. - - parameters : dict - - A dictionary with the parameters to override the default parameters of the workflow. - The structure of the dict is : - - { - : { - "parameter path 1(eg. a.b.c)" : value, - "parameter path 2(eg. a.b.c)" : value - . - . - . - } - } - - Returns - ------- - None - """ - logger = get_classMethod_logger(self,"addToGroup") - if workflow is None: - raise NotImplementedError("addToGroup() requires the 'hermes' library, which is nor installed") - logger.info("-- Start --") - - # 1. Getting the names of the simulation and the groups. - - # a. Make sure that there are no extensions. - cleanName = workflowJSON.split(".")[0] - - # b. loading the workflow. - self.logger.debug(f"Loading the workflow JSON {workflowJSON}") - hermesWF = workflow(loadJSON(workflowJSON), WD_path=self.FilesDirectory) - hermesWF.updateNodes(parameters=parameters) - theSolver = hermesWF.solver - - logger.debug(f"The suggested simulation name is {cleanName} as a workflow type {theSolver} (in file {workflowJSON})") - - # c. Determining the simulation name, group name and group id - groupName = groupName if groupName is not None else cleanName.split("_")[0] - if assignName: - self.logger.debug("Generating ID from the DB") - groupID, workflowName = self.findAvailableName(simulationGroup=groupName, workflowType=theSolver) - self.logger.debug(f" Got id : {groupID} and suggested name {workflowName}") - else: - workflowName = cleanName - try: - groupID = cleanName.split("_")[1] - if not groupID.isdigit(): - groupID = None - except IndexError: - # The name has no _ in it... - groupID = None - self.logger.debug(f"Use input as simulation : {workflowName} with the group {groupID}") - - self.logger.info(f"Simulation name is {workflowName} with type {theSolver} in simulation group {groupName} with id {groupID}.") - - # 2. Check if exists in the DB. - - # a. Check if the workflow exists in the DB under different name (assume similar type) - logger.debug(f"Checking if the workflow already exists in the db unde the same group and type.") - currentQuery = dictToMongoQuery(hermesWF.parametersJSON, prefix="parameters") - - docList = self.getWorkflowInGroup(groupName=groupName, **currentQuery) - - if len(docList) > 0 and (not force) and (docList[0]['desc']['workflowName'] != workflowName): - doc = docList[0] - wrn = f"The requested workflow {workflowName} has similar parameters to the workflow **{doc['desc']['workflowName']}** in simulation group {groupName}." - self.logger.warning(wrn) - raise FileExistsError(wrn) - else: - - # b. Check if the name of the simulation already exists in the group - self.logger.debug(f"Check if the name of the simulation {workflowName} already exists in the group") - docList = self.getWorkflowInGroup(groupName=groupName, workflowName=workflowName) - - if len(docList) == 0: - self.logger.info("Simulation is not in the DB, adding... ") - doc = self.addSimulationsDocument(resource=os.path.join(self.FilesDirectory, workflowName), - dataFormat=datatypes.STRING, - type=self.DOCTYPE_WORKFLOW, - desc=dict( - groupName=groupName, - groupID=groupID, - workflowName=workflowName, - solver=theSolver, - workflow=hermesWF.json, - parameters=hermesWF.parametersJSON) - ) - - elif overwrite: - self.logger.info("Simulation in the DB, overwrite=True. Updating... ") - doc = docList[0] - doc['desc']['workflow'] = hermesWF.json - doc['desc']['parameters'] = hermesWF.parametersJSON - doc.save() - else: - info = f"The simulation {workflowName} with type {theSolver} is already in the database in group {groupName}. use the overwrite=True to update the record." - self.logger.info(info) - - # 3. Building and running the workflow. - if execute: - logger.info(f"Building and executing the workflow {workflowName}") - build = hermesWF.build(buildername=workflow.BUILDER_LUIGI) - - logger.info(f"Writing the workflow and the executer python {workflowName}") - wfFileName = os.path.join(self.FilesDirectory, f"{workflowName}.json") - # attemp to write only if the file is different than the input (that is it exists) or if overwrite (which mean it needs to be updated). - if wfFileName != os.path.join(self.FilesDirectory,workflowJSON) or overwrite: - hermesWF.write(wfFileName) - with open(os.path.join(self.FilesDirectory, f"{workflowName}.py"), "w") as file: - file.write(build) - - # delete the run files if exist. - logger.debug(f"Removing the targetfiles and execute") - executionfileDir = os.path.join(self.FilesDirectory, f"{workflowName}_targetFiles") - shutil.rmtree(executionfileDir, ignore_errors=True) - - pythonPath = os.path.join(self.FilesDirectory, f"{workflowName}") - runDispatchId = dispatch_id or uuid.uuid4().hex - logger.info(f"Executing with scheduler='{scheduler}' dispatch_id='{runDispatchId}'") - executionStr = buildLuigiExecutionCommand(os.path.basename(pythonPath), - runDispatchId, - scheduler=scheduler, - schedulerHost=schedulerHost, - schedulerPort=schedulerPort) - self.logger.debug(executionStr) - subprocess.run(shlex.split(executionStr), check=True) - - - - def compareWorkflowObj(self, - workflowList, - longFormat : bool = False): - """ - Compares the parameters of the Workflow to each other. - - - Parameters - ---------- - workflowList : list of hermes workflow objects - The list of Workflow to compare. - Returns - ------- - - """ - return compareJSONS(**dict([(wf.name,wf.parametersJSON) for wf in workflowList]), - longFormat=longFormat) - - def compareWorkflow(self, - Workflow: Union[list, str], - longFormat : bool = False, - transpose : bool = False) -> Union[dict, pandas.DataFrame]: - """ - Compares two or more hermes Workflow. - - Parameters - ---------- - Workflow : str,list - A single input uses it as a group name, - a list is the list of Workflow names to compare. - - diffParams: bool - If true display only differences. - - JSON: bool - If true, return the results as a JSON and not pandas.DataFrame. - - Returns - ------- - pandas.DataFrame, json (depends on the input flags). - Return the differences between the parametrs of the requested Workflow. - """ - if Workflow is None: - raise NotImplementedError("compare() requires the 'hermes' library, which is nor installed") - self.logger.info("--- Start ---") - - workflowList = [] - for workflowName in list(Workflow): - if os.path.exists(workflowName): - workflowList.append(self.getHermesWorkflowFromJSON(workflowName,name=workflowName)) - else: - simulationList = self.getWorkflowListDocumentFromDB(workflowName) - groupworkflowList = [workflow(simulationDoc['desc']['workflow'],WD_path=self.FilesDirectory,name=simulationDoc.desc[self.DESC_WORKFLOWNAME]) for simulationDoc in simulationList] - workflowList+=groupworkflowList - - res = self.compareWorkflowObj(workflowList,longFormat=longFormat) - - return res.T if transpose else res - - def compareWorkflowInGroup(self, workflowGroup, longFormat=False, transpose=False) : - """ - Compares all the Workflow in the group name. - - Parameters - ---------- - workflowGroup : str - The group name. - - Returns - ------- - Pandas with the difference in the parameter names. - """ - simulationList = self.getWorkflowInGroup(groupName=workflowGroup) - workflowList = [workflow(simulationDoc['desc']['workflow'],WD_path=self.FilesDirectory,name=simulationDoc.desc[self.DESC_WORKFLOWNAME]) for simulationDoc in simulationList] - res = self.compareWorkflowObj(workflowList,longFormat=longFormat) - return res.T if transpose else res - - def listWorkflows(self, - workflowGroup:str, - listNodes : bool = False, - listParameters : bool = False) -> Union[pandas.DataFrame,dict]: - """ - Lists all the simulations in the simulation group (of this project). - - Allows additional filters using the simulationType. - - If parameters is not None, return the list of parameters. - return the parameters of all the nodes if the paraleters is an empty List, or the requested parameters. - The default behaviour is to return only the parameters that are different from each other, unless allParams - is True. - - The output is either pandas.DataFrame (if jsonFormat is False) or a JSON (if JSON is True). - - Parameters - ---------- - workflowGroup : str - The name of the group - - parametersOfNodes : list[str] - If None, just return the names of the simulations.old. Otherwise add the parameters from the requested nodes. - - allParams: bool - If true, list all the parameters and not just the parameters that were different between the simulations.old. - jsonFormat: bool - If true, return JSON and not a normalized pandas.DataFrame. - - Returns - ------- - pandas.DataFrame or dict - A list of the simulations.old and their values. - - """ - simulationList = self.getWorkflowInGroup(groupName=workflowGroup) - ret = [] - for simdoc in simulationList: - val = dict(workflowName=simdoc['desc']['workflowName']) - - if listNodes: - val['nodes'] = simdoc['desc']['workflow']['workflow']['nodeList'] - - if listParameters: - val['parameters'] = simdoc['desc']['parameters'] - - ret.append(val) - - return ret - - def listGroups(self, workflowType=None, workflowName=True): - """ - Lists all the simulation groups of the current project. - - Parameters - ---------- - - workflowType : str - The type of workflow to list. - If None, print all of them. - - workflowName : bool - if true, also lists all the simulations in that group. - - Returns - ------- - - """ - qry = dict(type=self.DOCTYPE_WORKFLOW) - if workflowType is not None: - qry['workflowType'] = workflowType - - docLists = self.getSimulationsDocuments(**qry) - if len(docLists)==0: - print(f"There are no workflow-groups in project {self.projectName}") - else: - data = pandas.DataFrame([dict(type=doc['desc']['workflowType'],workflowName=doc['desc']['workflowName'],groupName=doc['desc']['groupName']) for doc in docLists]) - - for (groupType,groupName),grpdata in data.groupby(["type","groupName"]): - ttl = f"{groupType}" - print(ttl) - print("-"*(len(ttl))) - print(f"\t* {groupName}") - if workflowName: - for simName in grpdata.workflowName.unique(): - print(f"\t\t + {simName}") - - - - - - - - - - - - - - - - - - - - diff --git a/hera/simulations/openFoam/postProcess/VTKPipelineExecutionContext.py b/hera/simulations/openFoam/postProcess/VTKPipelineExecutionContext.py deleted file mode 100755 index a0c093447..000000000 --- a/hera/simulations/openFoam/postProcess/VTKPipelineExecutionContext.py +++ /dev/null @@ -1,432 +0,0 @@ -import pandas -import os -import glob -from .. import CASETYPE_DECOMPOSED, TYPE_VTK_FILTER -from ....utils import loadJSON,get_classMethod_logger -from ....datalayer import datatypes -paraviewExists = False -try: - import paraview.simple as pvsimple - paraviewExists = True -except ImportError: - print("paraview not Found. Cannot execute the VTK pipeline.") - -from .pvOpenFOAMBase import paraviewOpenFOAM - -class VTKpipelineExecutionContext: - """ - - This is a helper class to execute VTK pipelines. - It has 2 main functions: - - * Execute the pipelines and save the results to parquet or netcdf file - Must run in the python 2.7 environment. - - * Loads the results to the database. - Must run in the python 3+ (with hera) environment. - - Currently works only for the JSON pipeline. The XML (paraview native pipelines) will be built in the future. - - The pipeline is initialized with a reader. - - The VTK pipeline JSON structure. - { - "metadata" : { - "guiname" : , - "timeList" : ... - }, - "pipeline" : { - "filterName" : { - "type" : The type of the filter. (clip,slice,...). - "write" : None/parquet (pandas)/netcdf (xarray), - "params" : [ - ("key","value"), - . - . - . - ],... - "downstream" : [Another pipeline] - } - } - } - The write to writes the requested filters to the disk. - Each filter is saved to a parquet/netcdf file. The file name is the filter name - """ - - _VTKpipelineJSON = None # Holds the json of the VTK pipeline. - _pvOFBase = None # Holds the OF base. - _casePath = None - _reader = None - - @property - def reader(self): - """The OpenFOAM reader proxy object.""" - return self._reader - - @property - def pvOFBase(self): - """The paraviewOpenFOAM base object.""" - return self._pvOFBase - - @property - def VTKpipelineJSON(self): - """The parsed JSON definition of the VTK pipeline.""" - return self._VTKpipelineJSON - - def __init__(self, pipelineJSON, casePath, caseType=CASETYPE_DECOMPOSED, serverName=None, fieldNames=None): - """ - Initializes a VTK pipeline. - - { - "metadata" : { - "fieldNames" : [optional] The field names to Load. - }, - "pipelines" : { - : { - "type" : , - "params" : [ - { - : - - } - ], - "write" : ['parquet','netcdf'], - "downstream" : { - .... filters... - } - } - } - } - - use parquet if non-regular data and netcdf for regular data. - - - Parameters - ---------- - datalayer : simulation.openFOAM toolkit - The data layer to use to load the pipeline. - - pipelineJSON : str,dict - the JSON (filename, or string) or dict. - - nameOrWorkflowFileOrJSONOrResource: str - - Resource (the directory name) - - Simulation name - - Its workflow - - workfolow dict. - - CaseType: str - Either 'Decomposed Case' for parallel cases or 'Reconstructed Case' - for single processor cases. - - fieldnames: None or list of field names. default: None. - The list of fields to load. - if None, read all fields - - servername: str - if None, work locally. - connection string to the paraview server. - - The connection string is printed when the server is initialized. - """ - logger = get_classMethod_logger(self,"__init__") - self._VTKpipelineJSON = loadJSON(pipelineJSON) - - self._serverName = serverName - self._caseType = caseType - - if paraviewExists: - self._pvOFBase = paraviewOpenFOAM(casePath=casePath, - caseType=caseType, - servername=self._serverName, - fieldNames=fieldNames) - - self._pvOFBase.parquetdir = os.path.abspath(os.path.join(casePath, 'parquet')) - self._pvOFBase.netcdfdir = os.path.abspath(os.path.join(casePath, "netcdf")) - - self._parquetdir = os.path.abspath(os.path.join(casePath, 'parquet')) - self._netcdfdir = os.path.abspath(os.path.join(casePath, "netcdf")) - - self._casePath = casePath - self._fieldNames = self._VTKpipelineJSON["metadata"].get("fieldNames", fieldNames) - - def initializeReader(self, readerName="reader"): - """ - Constructs a reader and register it in the vtk pipeline. - - Handles either parallel or single format. - - Parameters - ----------- - - readerName: str - The name of the reader. (of the pipline). - When using server, then you can have different pipelines with different names. - - casePath: str - a full path to the case directory. - CaseType: str - Either 'Decomposed Case' for parallel cases or 'Reconstructed Case' - for single processor cases. - fieldnames: list of str - List of field names to load. - if None, read all the fields. - - servername: str - The address of pvserver. If None, use the local single threaded case. - :return: - the reader - """ - self._readerName = readerName - self._reader = self.getReader(readerName=readerName) - - - def getReader(self, readerName="reader"): - """ - Constructs a reader and register it in the vtk pipeline. - - Handles either parallel or single format. - - Parameters - ----------- - - readerName: str - The name of the reader. (of the pipline). - When using server, then you can have different pipelines with different names. - - casePath: str - a full path to the case directory. - CaseType: str - Either 'Decomposed Case' for parallel cases or 'Reconstructed Case' - for single processor cases. - fieldnames: list of str - List of field names to load. - if None, read all the fields. - - servername: str - The address of pvserver. If None, use the local single threaded case. - :return: - the reader - """ - #self._readerName = readerName - reader = pvsimple.OpenFOAMReader(FileName="%s/tmp.foam" % self._casePath, CaseType=self._caseType, guiName=readerName) - reader.MeshRegions.SelectAll() - possibleRegions = list(reader.MeshRegions) - reader.MeshRegions = ['internalMesh'] - if self._fieldNames is not None: - reader.CellArrays = self._fieldNames - reader.UpdatePipeline() - - return reader - - - def execute(self, sourceOrName=None,timeList=None, tsBlockNum=50, overwrite=False,append=False): - """ - Executes the pipeline from the JSON vtk. - Saves the output of the requested filters. - - IF file exists, and it is without --append or --overwrite then raise exception. - - Parameters - ---------- - sourceOrName: str , None - A reader, the name of the reader, or none (to create reader). - - If reader, then assume that the server is already connected. - - timeList : list - The list of timesteps to read. - - tsBlockNum: int - The block number - overwrite : bool - If true, overwrite on the parquet. - - append : bool - If true, append to existing parquet - - Returns - ------- - None - """ - logger = get_classMethod_logger(self, "execute") - logger.info(f"Executing pipeline") - if sourceOrName is None: - reader = self.initializeReader(readerName="reader") - elif isinstance(sourceOrName,str): - logger.debug(f"Getting the reader {sourceOrName}") - reader = pvsimple.FindSource(sourceOrName) - if reader is None: - reader = self.initializeReader(readerName="reader") - else: - logger.debug(f"Got reader object: {sourceOrName}") - # assume server is connected. - reader = sourceOrName - - # build the pipeline. - filterWrite = {} - self._buildFilterLayer(father=reader, structureJson=self._VTKpipelineJSON["pipelines"], filterWrite=filterWrite) - # Now execute the pipeline. - - if timeList is None: - timelist = self._VTKpipelineJSON["metadata"].get("timelist", None) - - if timeList is not None and isinstance(timeList, str): - # a bit of parsing. - readerTL = reader.TimestepValues - BandA = [readerTL[0], readerTL[-1]] - - for i, val in enumerate(timeList.split(":")): - BandA[i] = BandA[i] if len(val) == 0 else float(val) - - tl = pandas.Series(readerTL) - timeList = tl[tl.between(*BandA)].values - - # Get the mesh regions. - if "MeshRegions" in self._VTKpipelineJSON["metadata"]: - reader.MeshRegions = self._VTKpipelineJSON["metadata"]["MeshRegions"] - - for frmt, datasourceslist in filterWrite.items(): - writer = getattr(self._pvOFBase, "write_%s" % frmt) - if writer is None: - raise ValueError("The write %s is not found" % writer) - writer(datasourcenamelist=datasourceslist, - timeList=timeList, - fieldnames=self._fieldNames, - tsBlockNum=tsBlockNum, - overwrite=overwrite, - append=append) - - def _buildFilterLayer(self, father, structureJson, filterWrite): - """ - Recursively builds the structure of the leaf. - Populates the self._filterWrite map - - Since the order of setting the params might be of importance (for example, setting the - plane type determine the rest of the parameters), we set it as a list. - - :param father: - The current filter father of the layer. - - :param structureJson: - The portion of Json to build. - - :param[output] filterWrite - an dictionary with the names of the filters that are about - to be printed according to format. - - """ - logger = get_classMethod_logger(self, "_buildFilterLayer") - logger.info(f"building Filter layer {structureJson}") - if structureJson is None: - return - for filterGuiName in structureJson: - - paramPairList = structureJson[filterGuiName]['params'] # must be a list to enforce order in setting. - filtertype = structureJson[filterGuiName]['type'] - filter = getattr(pvsimple, filtertype)(Input=father, guiName=filterGuiName) - logger.debug(f"Adding filter {filterGuiName} of type {filtertype} to {father}") - for param, pvalue in paramPairList: - logger.debug(f"...Adding parameters {param} with value {pvalue}") - #pvalue = str(pvalue) if isinstance(pvalue, unicode) else pvalue # python2, will be removed in python3. - paramnamelist = param.split(".") - paramobj = filter - for pname in paramnamelist[:-1]: - paramobj = getattr(paramobj, pname) - setattr(paramobj, paramnamelist[-1], pvalue) - filter.UpdatePipeline() - writeformat = structureJson[filterGuiName].get("write", None) - - if (writeformat is not None) and (str(writeformat) != "None"): - filterlist = filterWrite.setdefault(writeformat, []) - filterlist.append(filterGuiName) - - self._buildFilterLayer(filter, structureJson[filterGuiName].get("downstream", None), filterWrite) - - -def execute(self, sourceOrName=None, timeList=None, tsBlockNum=50, overwrite=False, append=False): - """ - Returns a VTK Execution that will allow the execution of the VTK pipeline. - - We use the execution to let the user access the reader (and thus, the timesteps). - - Parameters - ---------- - sourceOrName: str , None - A reader, the name of the reader, or none (to create reader). - - If reader, then assume that the server is already connected. - - timeList : list - The list of timesteps to read. - - tsBlockNum: int - The block number - overwrite : bool - If true, overwrite on the parquet. - - append : bool - If true, append to existing parquet - - Returns - ------- - - """ - logger = get_classMethod_logger(self, "execute") - - if self.executionContext is None: - err = f"The execution context is not set. use the method setExecutionContext to set it" - logger.error(err) - raise ValueError(err) - - self.executionContext.execute(sourceOrName=sourceOrName, timeList=timeList, tsBlockNum=tsBlockNum, - overwrite=overwrite, append=append) - - -def toJSON(self): - """ - Converts the pipeline to a VTK JSON of the executions. - - { - "filterName" : { - "type" : The type of the filter. (clip,slice,...). - "write" : None/parquet (pandas)/netcdf (xarray), - "params" : [ - ("key","value"), - . - . - . - ],... - "downstream" : [Another pipeline] - } - }, - } - - Returns - ------- - - """ - retDict = dict() - for filterName, filterData in self.filters.items(): - retDict.update(filterData.toJSON()) - - return dict(metadata=dict(timeList=self.timeList, casePath=self.casePath), - pipelines=retDict) - - -def writeJSON(self, fileName): - """ - Writes the pipeline to a JSON file. - Parameters - ---------- - fileName - - Returns - ------- - - """ - if '.json' not in fileName: - fileName = f'{fileName}.json' - - with open(fileName, 'w') as outfile: - json.dump(self.toJSON(), fileName, indent=4) diff --git a/hera/simulations/openFoam/preprocessOFObjects/OFList.py b/hera/simulations/openFoam/preprocessOFObjects/OFList.py deleted file mode 100644 index a9456d1be..000000000 --- a/hera/simulations/openFoam/preprocessOFObjects/OFList.py +++ /dev/null @@ -1,71 +0,0 @@ -import pandas -from .OFObject import OFObject - - - -class OFList(OFObject): - """ - Just data. - """ - - def _updateExisting(self, filename, data, parallel=False): - """ - Just rewrite the field. - - This function exists to complete the interface similarly to field. - - - Parameters - ---------- - filename: str - The file name - data : str - - Returns - ------- - - """ - return self._writeNew(filename, data, parallel=parallel) - - def _writeNew(self, filename, data, parallel=False): - """ - Writes an OF list file. - - Parameters - ---------- - filename : str - The name of the file - - data: pandas.DataFrame or pandas.Series - Holds the data - - columnNames: list [optional] - The list of names to use. If None, use all. - - Returns - ------- - str, - """ - if isinstance(data, pandas.Series): - columnNames = ['demo'] - else: - columnNames = [x for x in data.columns if - (x != 'processor' and x != 'time')] if self.columnNames is None else self.columnNames - - fileStrContent = self.getHeader() - if len(columnNames) > 1: - # vector - fileStrContent += self.pandasToFoamFormat(data, columnNames) - - else: - # scalar - if isinstance(data, pandas.Series): - fileStrContent += "\n".join(data) - else: - fileStrContent += "\n".join(data[columnNames]) - - with open(filename, 'w') as outfile: - outfile.write(fileStrContent) - - return fileStrContent - diff --git a/hera/simulations/openFoam/preprocessOFObjects/__init__.py b/hera/simulations/openFoam/preprocessOFObjects/__init__.py index 93d9f8d45..954754025 100644 --- a/hera/simulations/openFoam/preprocessOFObjects/__init__.py +++ b/hera/simulations/openFoam/preprocessOFObjects/__init__.py @@ -1,5 +1,4 @@ from .OFObject import OFObject -from .OFList import OFList from .OFObjectHome import OFObjectHome from .OFField import OFField from .utils import extractFieldFile,ParsedParameterFileToDataFrame \ No newline at end of file diff --git a/hera/simulations/utils/interpolation/__init__.py b/hera/simulations/utils/interpolation/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/hera/simulations/utils/interpolation/interpolations.py b/hera/simulations/utils/interpolation/interpolations.py deleted file mode 100644 index 5067f0db2..000000000 --- a/hera/simulations/utils/interpolation/interpolations.py +++ /dev/null @@ -1,286 +0,0 @@ -import numpy -import math -import pandas - -class interpulations(): - - def interp(self, point, stations,topography=None, dx=20, dy=20, C=1000, D=5, Hsl=100, b=150): - """ - Interpulate the values of a variable by its values in different stations. - params: - point: The point ([x,y,z]) - topography: The height of the topography at the point - stations: list of information from stations. Each value in the list - is of the structure [latitude, longitude, elevation,[values of variable]] - dx,dy: Measures of each cell - C: The value of parameter C, which represent the measure of influence between layers in the atmospheres high above the ground. - D: The value of parameter D, which represent the measure of influence between layers in the atmospheres near the ground. - Hsl: The value of parameter Hsl, which effect the height above ground in which the behavor changes from D to C. - b: The value of parameter b, which effect the slope of the change from D to C. - """ - - vector = True if type(stations[0][3])==list else False - a_squared = 0.25*(dx**2+dy**2) - lat = point[0] - lon = point[1] - elev = point[2] - if topography is None: - Ctag=C - else: - h = elev-topography - Ctag = D/(-numpy.tanh((h-Hsl/2)/b)/2+0.5+D/C) - for n in range(len(stations)): - if stations[n][0] == lat and stations[n][1] == lon and stations[n][2] == elev: - return stations[n][3] - wt = 0 - value = [0 for i in range(len(stations[0][3]))] if vector else 0 - for n in range(len(stations)): - r = float((stations[n][0] - lat) ** 2 + (stations[n][1] - lon) ** 2) ** 0.5 - widw = 1.0 / (1. + (r**2)/a_squared) - dz = math.fabs(stations[n][2] - elev) - wedw = 1. /(1.+Ctag*(dz**2./a_squared)) - wt += widw*wedw - for n in range(len(stations)): - r = float((stations[n][0] - lat) ** 2 + (stations[n][1] - lon) ** 2) ** 0.5 - widw = 1.0 / (1. + (r**2)/a_squared) - dz = math.fabs(stations[n][2] - elev) - wedw = 1. /(1.+Ctag*(dz**2./a_squared)) - wb = widw*wedw - if vector: - for i in range(len(stations[0][3])): - value[i] += wb * stations[n][3][i] / wt - else: - value += wb * stations[n][3] / wt - - return value - - def checkInterpulation(self, stations, dx=20, dy=20, C=1000, D=5, Hsl=100, b=150): - """ - Gets a list of stations, and performs an interpulation of the values in each station - by the information of the other stations. - Returns a list of the differences in percentage between the measured and interpulated values. - """ - differences = [] - for station in stations: - point = [station[0],station[1],station[2]] - newStations = stations.copy() - newStations.remove(station) - inter = self.interp(point=point, topography=point[2], stations=newStations, dx=dx, dy=dy, C=C, D=D, Hsl=Hsl, b=b) - measure = station[3] - difference = [] - for i in range(len(inter)): - difference.append((inter[i]-measure[i])/measure[i]*100) - differences.append(difference) - - return differences - - def interpPandas(self, points, stations, columnNames={"x":"x","y":"y","z":"z","topography":"topography"}, dx=20, dy=20, C=1000, D=5, Hsl=100, b=150): - - points = points.reset_index() - points["interpulation"] = None - for i in range(len(points)): - point = [points[columnNames["x"]][i],points[columnNames["y"]][i],points[columnNames["z"]][i]] - topography = points[columnNames["topography"]][i] if columnNames["topography"] in points.columns else None - points["interpulation"][i] = self.interp(point=point, stations=stations, topography=topography, - dx=dx, dy=dy, C=C, D=D, Hsl=Hsl, b=b) - return points - - def interpArray(self, points, stations, columnNames={"x":"x","y":"y","z":"z","topography":"topography"}, dx=20, dy=20, C=1000, D=5, Hsl=100, b=150): - - newPoints = pandas.DataFrame({"x":points[columnNames["x"]], - "y":points[columnNames["y"]], - "z":points[columnNames["z"]], - "topography":points[columnNames["topography"]]}) - return self.interpPandas(points=newPoints, stations=stations, topography=newPoints["topography"], - dx=dx, dy=dy, C=C, D=D, Hsl=Hsl, b=b) - - - def windprofile(self, z, uref=3, href=24, he=24, lambdap=0.3, lambdaf=0.3,beta=0.3): - - # This function will return the wind velocity at height z - - # parameters: - # z is the predicted height [m] above the ground - # uref is the velocity reference [m/s] at height href - # href is the reference height [m] of uref - # he is the mean building height [m] above the ground - # lambdap is the building percent when looking from top - # lambdaf is the building percent when looking from the side - # beta is the ratio between ustar and Uh - - # return - # u is the predicted velocity [m/s] that is calculated in this function - - # variables: - # l is the mixing length [m] - - ############################### - ## z=24 - # uref=3 - # href=24 - # he=24 - # lambdap=0.3 - # lambdaf=0.3 - ############################### - - k = 0.41 # von Karman constant - # beta = beta# For closed uniform natural canopies, Raupach 1996 - # beta = ustar / uh - # LA commercial area has lambdap=0.28, lambdaf=0.27 he=24.5m, LC=66, Coceal 2004 - lc = he*(1-lambdap)/lambdaf - # calculating the mixing length at the canopy only to know d - l = 2. * beta**3 * lc # mixing length in the canopy - d = l/k # displacement height - z0 = l/k*math.exp(-k/beta) # surface roughness - - if href>=he: - uh = uref * k /beta / math.log(((href-he)+d)/z0) - else: # href