diff --git a/bl/menus.py b/bl/menus.py index ad05afe..8e10f43 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,147 @@ 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" + ) + + 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.prop(self, "cloudHPC_dirname") + + col = self.layout.column(align = True) + col.operator("object.simple_operator") + + 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, 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"} + + 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 a CloudHPC Key, 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 +357,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 +370,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) diff --git a/lang.py b/lang.py index 7d93d5f..8f63670 100644 --- a/lang.py +++ b/lang.py @@ -1943,75 +1943,120 @@ 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) - # 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 - # ijk = ob.bf_mesh_ijk - # ijk_split = ob.bf_mesh_ijk_split - # 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], - # ) - # # Split xbs - - # # Single param - # if len(xbs) == 1: - # return FDSParam(fds_label="XB", value=xbs[0], precision=6) - # # 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": - # ids = (f"{n}_x{xb[0]:+.3f}" for xb in xbs) - # elif suffix == "IDY": - # ids = (f"{n}_y{xb[2]:+.3f}" for xb in xbs) - # elif suffix == "IDZ": - # ids = (f"{n}_z{xb[4]:+.3f}" for xb in xbs) - # elif suffix == "IDXY": - # ids = (f"{n}_x{xb[0]:+.3f}_y{xb[2]:+.3f}" for xb in xbs) - # elif suffix == "IDXZ": - # ids = (f"{n}_x{xb[0]:+.3f}_z{xb[4]:+.3f}" for xb in xbs) - # elif suffix == "IDYZ": - # ids = (f"{n}_y{xb[2]:+.3f}_z{xb[4]:+.3f}" for xb in xbs) - # elif suffix == "IDXYZ": - # 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 - # # Prepare multi fds_param - # result = tuple( - # ( - # FDSParam(fds_label="ID", value=hid), - # 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 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 ): +# +# # Detection of IJK and SPLIT (if not present by default split = 1, 1, 1 +# ijk = ob.bf_mesh_ijk +# +# 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] - 1 ), +# ijk[1] - ijk_first[1] * ( ijk_split[1] - 1 ), +# ijk[2] - ijk_first[2] * ( ijk_split[2] - 1 ), +# ) +# # 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": +# ids = (f"{n}_x{xb[0]:+.3f}" for xb in xbs) +# elif suffix == "IDY": +# ids = (f"{n}_y{xb[2]:+.3f}" for xb in xbs) +# elif suffix == "IDZ": +# ids = (f"{n}_z{xb[4]:+.3f}" for xb in xbs) +# elif suffix == "IDXY": +# ids = (f"{n}_x{xb[0]:+.3f}_y{xb[2]:+.3f}" for xb in xbs) +# elif suffix == "IDXZ": +# ids = (f"{n}_x{xb[0]:+.3f}_z{xb[4]:+.3f}" for xb in xbs) +# elif suffix == "IDYZ": +# ids = (f"{n}_y{xb[2]:+.3f}_z{xb[4]:+.3f}" for xb in xbs) +# elif suffix == "IDXYZ": +# 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}>") +# +# ( +# 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, 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) +# ) +# +# return result def draw(self, context, layout): ob = self.element @@ -3185,7 +3230,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 @@ -3210,20 +3254,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): @@ -3235,7 +3283,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):