From aff25248b934e347035b15ce1bae4793945a73c9 Mon Sep 17 00:00:00 2001 From: Ruggero Date: Sat, 3 Oct 2020 16:11:47 +0200 Subject: [PATCH 1/3] Split mesh in all directions --- lang.py | 116 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/lang.py b/lang.py index 7b364c6..4a6472c 100644 --- a/lang.py +++ b/lang.py @@ -1852,6 +1852,7 @@ def to_fds_param(self, context): ob.bf_xb = "BBOX" if not ob.bf_xb_export: return + # Compute xbs, _ = geometry.to_fds.ob_to_xbs(context, ob) return FDSParam(fds_label="XB", value=xbs[0], precision=6) @@ -1860,34 +1861,73 @@ def to_fds_param(self, context): ob = self.element if not ob.bf_xb_export: return + # Compute ob.bf_xb = "BBOX" # len(xbs) == 1 xbs, msg = geometry.to_fds.ob_to_xbs(context, ob) + # Split FIXME FIXME FIXME - if ( - ob.bf_mesh_ijk_export and ob.bf_mesh_ijk_split_export - ): # FIXME new properties - # Split ijk + if ( ob.bf_mesh_ijk_export ): + + # Detection of IJK and SPLIT (if not present by default split = 1, 1, 1 ijk = ob.bf_mesh_ijk - ijk_split = ob.bf_mesh_ijk_split + + if ( ob.bf_mesh_split_export ): + ijk_split = ob.bf_mesh_split + else: + ijk_split = ( 1, 1, 1) + + #IJK calculation - we take the floor in case of rest and the last bit takes the remaining ijk_first = ( ijk[0] // ijk_split[0], ijk[1] // ijk_split[1], ijk[2] // ijk_split[2], ) ijk_last = ( - ijk[0] - ijk_first[0] * ijk_split[0], - ijk[1] - ijk_first[1] * ijk_split[1], - ijk[2] - ijk_first[2] * ijk_split[2], + ijk[0] - ijk_first[0] * ( ijk_split[0] - 1 ), + ijk[1] - ijk_first[1] * ( ijk_split[1] - 1 ), + ijk[2] - ijk_first[2] * ( ijk_split[2] - 1 ), ) - # Split xbs - - # Single param - if len(xbs) == 1: - return FDSParam(fds_label="XB", value=xbs[0], precision=6) + # Definition of array of XBS (BB) and IJK for all the new meshes + xbs_global = list( xbs[0] ) + xbs.clear() + ijks = [] + + for i in range( ijk_split[0]): + for j in range( ijk_split[1]): + for k in range( ijk_split[2]): + + xbs_local = [None] * 6 + ijk_local = [None] * 3 + + ijk_local = list( ijk_first ) + + xbs_local[0] = xbs_global[0] + i * ( xbs_global[1] - xbs_global[0] ) / ijk[0] * ijk_first[0] + xbs_local[1] = xbs_local[0] + ( xbs_global[1] - xbs_global[0] ) / ijk[0] * ijk_first[0] + xbs_local[2] = xbs_global[2] + j * ( xbs_global[3] - xbs_global[2] ) / ijk[1] * ijk_first[1] + xbs_local[3] = xbs_local[2] + ( xbs_global[3] - xbs_global[2] ) / ijk[1] * ijk_first[1] + xbs_local[4] = xbs_global[4] + k * ( xbs_global[5] - xbs_global[4] ) / ijk[2] * ijk_first[2] + xbs_local[5] = xbs_local[4] + ( xbs_global[5] - xbs_global[4] ) / ijk[2] * ijk_first[2] + + if ( i == ijk_split[0] - 1 ): + xbs_local[1] = xbs_global[1] + ijk_local[0] = ijk_last[0] + + if ( j == ijk_split[1] - 1 ): + xbs_local[3] = xbs_global[3] + ijk_local[1] = ijk_last[1] + + if ( k == ijk_split[2] - 1 ): + xbs_local[5] = xbs_global[5] + ijk_local[2] = ijk_last[2] + + xbs.append( xbs_local ) + ijks.append( ijk_local ) + # Multi param, prepare new ID n = ob.name suffix = self.element.bf_id_suffix + if suffix == "IDI": ids = (f"{n}_{i}" for i, _ in enumerate(xbs)) elif suffix == "IDX": @@ -1906,20 +1946,25 @@ def to_fds_param(self, context): ids = (f"{n}_x{xb[0]:+.3f}_y{xb[2]:+.3f}_z{xb[4]:+.3f}" for xb in xbs) else: raise AssertionError(f"Unknown suffix <{suffix}>") - # Set ijks - ijks = (ijk_first for xb in xbs) - ijks[-1] = ijk_last + + ( + has_good_ijk, + cs, + cell_count, + cell_aspect_ratio, + ) = fds.mesh_tools.calc_cell_infos(ijk=ob.bf_mesh_ijk, xb=xbs_global) + msg = f"MESH Cell Size: {cs[0]:.3f} m, {cs[1]:.3f} m, {cs[2]:.3f} m | Qty: {cell_count} | Aspect: {cell_aspect_ratio:.1f} | Poisson: {has_good_ijk and 'Yes' or 'No'}" + # Prepare multi fds_param result = tuple( ( - FDSParam(fds_label="ID", value=hid), + FDSParam(fds_label="ID", value=hid, msg=msg), FDSParam(fds_label="IJK", value=ijk), FDSParam(fds_label="XB", value=xb, precision=6), ) for hid, ijk, xb in zip(ids, ijks, xbs) ) - # Send message - result[0][0].msg = msg + return result def draw(self, context, layout): @@ -3082,7 +3127,6 @@ class OP_MESH_IJK(BFParam): label = "IJK" description = "Cell number in x, y, and z direction" - fds_label = "IJK" bpy_type = Object bpy_idname = "bf_mesh_ijk" bpy_prop = IntVectorProperty @@ -3107,20 +3151,24 @@ def draw(self, context, layout): ) super().draw(context, layout) - def to_fds_param(self, context): - ob = self.element - if not ob.bf_mesh_ijk_export: - return - xb = geometry.utils.get_bbox_xb(context=context, ob=ob, world=True) - ( - has_good_ijk, - cs, - cell_count, - cell_aspect_ratio, - ) = fds.mesh_tools.calc_cell_infos(ijk=ob.bf_mesh_ijk, xb=xb) - msg = f"MESH Cell Size: {cs[0]:.3f} m, {cs[1]:.3f} m, {cs[2]:.3f} m | Qty: {cell_count} | Aspect: {cell_aspect_ratio:.1f} | Poisson: {has_good_ijk and 'Yes' or 'No'}" - return FDSParam(fds_label="IJK", value=ob.bf_mesh_ijk, msg=msg) +@subscribe +class OP_MESH_SPLIT(BFParam): + """! + Blender definition of the split feature of the MESH + """ + label = "Split" + description = "Split current mesh in X, Y or Z direction" + bpy_type = Object + bpy_idname = "bf_mesh_split" + bpy_prop = IntVectorProperty + bpy_default = (1, 1, 1) + bpy_other = {"size": 3, "min": 1} + bpy_export = "bf_mesh_split_export" + bpy_export_default = False + + def draw(self, context, layout): + super().draw(context, layout) @subscribe class ON_MESH(BFNamelistOb): @@ -3132,7 +3180,7 @@ class ON_MESH(BFNamelistOb): description = "Domain of simulation" enum_id = 1014 fds_label = "MESH" - bf_params = OP_ID, OP_FYI, OP_MESH_IJK, OP_XB_BBOX, OP_other + bf_params = OP_ID, OP_FYI, OP_MESH_IJK, OP_MESH_SPLIT, OP_MESH_XB_BBOX, OP_other bf_other = {"appearance": "WIRE"} def draw_operators(self, context, layout): From d98de8474cb19175e37ce835732e768327ba2a11 Mon Sep 17 00:00:00 2001 From: carlopoletto Date: Sun, 11 Oct 2020 18:54:39 +0200 Subject: [PATCH 2/3] integration of uploading files to CloudHPC --- bl/menus.py | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/bl/menus.py b/bl/menus.py index 15aed9f..12d715c 100644 --- a/bl/menus.py +++ b/bl/menus.py @@ -4,6 +4,10 @@ import os +import json +import tempfile +import webbrowser +import urllib3 import bpy, logging from bpy.types import Operator from bpy.props import StringProperty, BoolProperty, FloatProperty @@ -199,6 +203,155 @@ def menu_func_export_to_fds(self, context): self.layout.operator(ExportFDS.bl_idname, text="NIST FDS (.fds)") +@subscribe +class ExportFDSCloudHPC(Operator): + """! + Export current Scene to FDS case file. + """ + + bl_idname = "export_scene_cloudhpc.fds" + bl_label = "Export FDS into CloudHPC" + bl_description = "Export Blender Scenes into CloudHPC" + + cloudKeyFilePath = "/".join((os.path.dirname(os.path.realpath(__file__)), "CloudHPCKey.txt")) + + cloudHPC_key = bpy.props.StringProperty( + name = "CloudHPC Key", + default = "" + ) + + cloudHPC_dirname = bpy.props.StringProperty( + name = "CloudHPC Directory", + default = "Directory" + ) + + cloudHPC_filename = bpy.props.StringProperty( + name = "CloudHPC Filename", + default = "Filename.fds" + ) + + def _getCloudKey(self): + try: + with open(self.cloudKeyFilePath, "r") as f: + return f.read() + except: + return "" + + def invoke(self, context, event): + self.cloudHPC_key = self._getCloudKey() + return context.window_manager.invoke_props_dialog(self, width = 450) + + def draw(self, context): + col = self.layout.column(align = True) + col.prop(self, "cloudHPC_key") + + col = self.layout.column(align = True) + col.operator("object.simple_operator") + + col = self.layout.column(align = True) + col.prop(self, "cloudHPC_dirname") + + col = self.layout.column(align = True) + col.prop(self, "cloudHPC_filename") + + def execute(self, context): + with tempfile.NamedTemporaryFile(mode='w+', suffix='.fds', delete=True) as temp: + #try: + with open(self.cloudKeyFilePath, "w") as f: + f.write(self.cloudHPC_key) + + temp.writelines(context.scene.to_fds(context=context, full=True)) + temp.seek(0) + self._upload_fds(self.cloudHPC_key, self.cloudHPC_dirname, self.cloudHPC_filename, temp) + ShowMessageBox(message="File was uploaded succesfully", title="CFD Fea Service") + webbrowser.open('https://cloud.cfdfeaservice.it/', new=2) + return {"FINISHED"} + + #except Exception as e: + # ShowMessageBox(message="An error occurred during the file upload", title="CFD Fea Service") + # return {"FINISHED"} + + def _upload_fds(self, api_key, dirname, filename, fdsFile): + + headers = { + 'api-key': f'{api_key}', + 'Content-Type': 'application/json', + 'Accept': 'application/json', + } + + body = json.dumps({ + 'data': { + 'dirname': f'{dirname}', + 'basename': f'{filename}', + 'contentType': 'application/octet-stream' + } + }) + + http = urllib3.PoolManager() + response = http.request( + 'POST', + 'https://cloud.cfdfeaservice.it/api/v1/storage/upload/url', + headers=headers, + body=body + ) + + print(response.status) + if(response.status != 200): + raise ConnectionError(response.status) + + #--------------- + + url = json.loads(response.data.decode('utf-8'))["url"] + + headers = { + 'Content-Type': 'application/octet-stream', + } + + body = fdsFile.read() + + http = urllib3.PoolManager() + response = http.request( + 'PUT', + url, + headers=headers, + body=body + ) + + print(response.status) + if(response.status != 200): + raise ConnectionError(response.status) + + +def menu_func_export_to_cloudHPC(self, context): + """! + Export function for current Scene to FDS case file. + """ + self.layout.operator(ExportFDSCloudHPC.bl_idname, text="FDS (.fds) into CloudHPC") + + +def ShowMessageBox(message="", title="Message Box", icon='INFO'): + + def draw(self, context): + self.layout.label(text=message) + + bpy.context.window_manager.popup_menu(draw, title=title, icon=icon) + + +@subscribe +class SimpleOperator(Operator): + """Tooltip""" + + bl_idname = "object.simple_operator" # <- put this string in layout.operator() + bl_label = "If you don't have one, register here" # <- button name + + @classmethod + def poll(cls, context): + return context.active_object is not None + + def execute(self, context): + webbrowser.open('https://cloudhpc.cloud/form-request/', new=2) + return {'FINISHED'} + # Register @@ -212,6 +365,7 @@ def register(): register_class(cls) bpy.types.TOPBAR_MT_file_import.append(menu_func_import_FDS) bpy.types.TOPBAR_MT_file_export.append(menu_func_export_to_fds) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export_to_cloudHPC) def unregister(): @@ -224,3 +378,4 @@ def unregister(): unregister_class(cls) bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_FDS) bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_to_fds) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_to_cloudHPC) From 84d8fec47a1b1c36d4f70f6c6db26cffe8231acf Mon Sep 17 00:00:00 2001 From: carlopoletto Date: Sun, 25 Oct 2020 14:31:23 +0100 Subject: [PATCH 3/3] CloudHPC integration: using CHID as filename --- bl/menus.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/bl/menus.py b/bl/menus.py index 12d715c..25b2e2a 100644 --- a/bl/menus.py +++ b/bl/menus.py @@ -225,11 +225,6 @@ class ExportFDSCloudHPC(Operator): default = "Directory" ) - cloudHPC_filename = bpy.props.StringProperty( - name = "CloudHPC Filename", - default = "Filename.fds" - ) - def _getCloudKey(self): try: with open(self.cloudKeyFilePath, "r") as f: @@ -245,31 +240,28 @@ def draw(self, context): col = self.layout.column(align = True) col.prop(self, "cloudHPC_key") - col = self.layout.column(align = True) - col.operator("object.simple_operator") - col = self.layout.column(align = True) col.prop(self, "cloudHPC_dirname") col = self.layout.column(align = True) - col.prop(self, "cloudHPC_filename") + col.operator("object.simple_operator") def execute(self, context): with tempfile.NamedTemporaryFile(mode='w+', suffix='.fds', delete=True) as temp: - #try: + try: with open(self.cloudKeyFilePath, "w") as f: f.write(self.cloudHPC_key) temp.writelines(context.scene.to_fds(context=context, full=True)) temp.seek(0) - self._upload_fds(self.cloudHPC_key, self.cloudHPC_dirname, self.cloudHPC_filename, temp) + self._upload_fds(self.cloudHPC_key, self.cloudHPC_dirname, context.scene.name + ".fds", temp) ShowMessageBox(message="File was uploaded succesfully", title="CFD Fea Service") webbrowser.open('https://cloud.cfdfeaservice.it/', new=2) return {"FINISHED"} - #except Exception as e: - # ShowMessageBox(message="An error occurred during the file upload", title="CFD Fea Service") - # return {"FINISHED"} + except Exception as e: + ShowMessageBox(message="An error occurred during the file upload", title="CFD Fea Service") + return {"FINISHED"} def _upload_fds(self, api_key, dirname, filename, fdsFile): @@ -342,7 +334,7 @@ class SimpleOperator(Operator): """Tooltip""" bl_idname = "object.simple_operator" # <- put this string in layout.operator() - bl_label = "If you don't have one, register here" # <- button name + bl_label = "If you don't have a CloudHPC Key, register here" # <- button name @classmethod def poll(cls, context):