diff --git a/config.py b/config.py index a68c6779240763255e1f670be2cb0417ee72be90..1e9cde2a5ae79788fed5784d04cd3bbe589c9828 100644 --- a/config.py +++ b/config.py @@ -235,7 +235,11 @@ def write_namespace(file, ns, ns_name, indent): if isinstance(ns[key], dict): write_namespace(file, ns[key], key, indent + 4) else: - file.write(" " * (indent + 4) + f"{key} = {get_str_from_value(ns[key].value, ns[key].value_type)};\n") + if ns[key].comment: + line = " " * (indent + 4) + f"{key} = {get_str_from_value(ns[key].value, ns[key].value_type)}; # {ns[key].comment}\n" + else: + line = " " * (indent + 4) + f"{key} = {get_str_from_value(ns[key].value, ns[key].value_type)};\n" + file.write(line) file.write(" " * indent + "}\n") file.write("\n") @@ -248,7 +252,11 @@ def dump_config(config, file_path): if isinstance(config_nested[key], dict): write_namespace(file, config_nested[key], key, indent) else: - file.write(f"{key} = {get_str_from_value(config_nested[key].value, config_nested[key].value_type)};\n") + if config_nested[key].comment: + line = f"{key} = {get_str_from_value(config_nested[key].value, config_nested[key].value_type)}; # {config_nested[key].comment}\n" + else: + line = f"{key} = {get_str_from_value(config_nested[key].value, config_nested[key].value_type)};\n" + file.write(line) if __name__ == '__main__': diff --git a/config_callbacks.py b/config_callbacks.py index 967e4b991c7aff6bbc33f4bc889e9ea9e8ba0cb3..71a80f65d28f42fa0c0cf8311a4d7f5df5387388 100644 --- a/config_callbacks.py +++ b/config_callbacks.py @@ -6,6 +6,15 @@ from style_constants import SPACE, COLLAPSING_HEADERS_UI from style import get_accent_color, apply_theme from config import load_config, dump_config, ConfigParserError from dynamic_ui import construct_config_ui, construct_model_output_structure_ui +from map_edit_callbacks import open_map + +def show_map_window_clb(s, a, u): + if get_config_file_path(): + config = get_config() + if 'topography.filename' in config: + filename = config['topography.filename'].value + open_map(filename) + dpg.show_item('map_window') def set_file_path_clb(s, a, u): file_path = a['file_path_name'] diff --git a/config_ui.py b/config_ui.py index 69c389611cc1b14aeda5a480c6f452d878e81d98..87e0b67297a7886e314295ac029c04526dfe8216 100644 --- a/config_ui.py +++ b/config_ui.py @@ -14,7 +14,7 @@ def construct_main_window_ui(): dpg.add_menu_item(label='save\t\t', shortcut='ctrl+s', callback=config_save_clb, tag='save_config') dpg.add_menu_item(label='save as\t\t', shortcut='ctrl+shift+s', callback=open_config_save_as_dialog_clb, tag='open_config_save_as_dialog') - dpg.add_menu_item(label='map', callback=lambda: dpg.show_item('map_window')) + dpg.add_menu_item(label='map', callback=show_map_window_clb) dpg.add_menu_item(label='run', callback=lambda: dpg.show_item('run_window')) with dpg.menu(label='view'): diff --git a/constants.py b/constants.py deleted file mode 100644 index 28d924d673225bb3c0da17f43c524dfc691ac59d..0000000000000000000000000000000000000000 --- a/constants.py +++ /dev/null @@ -1,86 +0,0 @@ -import os - -from config import CfgVar - -ROAD_COLOR = (221, 161, 94, 255) -TREE_COLOR = (96, 108, 56, 255) -BUILDING_COLOR = (188, 108, 37, 255) -EMISSION_POINT_COLOR = (255, 0, 0, 255) - -TABS_UI = 'tabs_ui' -COLLAPSING_HEADERS_UI = 'collapsing_headers_ui' -WIDGET_WIDTH = 300 -XOFFSET = 300 -LCZS = ['compact_high_rise', 'compact_low_rise', 'compact_mid_rise', 'heavy_industry', 'large_low_rise', 'lightweight_low_rise', 'open_high_rise', 'open_low_rise', 'open_mid_rise', 'sparsley_build'] - -MAX_MAP_WIDTH = 2000 -MAX_MAP_HEIGHT = 2000 - -EMISSION_POINT_CONFIG_VARIABLES = { - 'value': CfgVar(key='value', value=28481176.531511, value_type='DOUBLE', comment=''), - 'begin': CfgVar(key='begin', value=25200.0, value_type='DOUBLE', comment='[s]'), - 'xpos': CfgVar(key='xpos', value=200.0, value_type='DOUBLE', comment='[m]'), - 'ypos': CfgVar(key='ypos', value=200.0, value_type='DOUBLE', comment='[m]'), - 'zpos': CfgVar(key='zpos', value=60.0, value_type='DOUBLE', comment='[m]'), - 'sx': CfgVar(key='sx', value=20.0, value_type='DOUBLE', comment='[m]'), - 'sy': CfgVar(key='sy', value=20.0, value_type='DOUBLE', comment='[m]'), - 'sz': CfgVar(key='sz', value=10.0, value_type='DOUBLE', comment='[m]') -} - -UIS = [TABS_UI, COLLAPSING_HEADERS_UI] - -FONT_PATH = os.path.join('fonts', 'Montserrat-Medium.ttf') -ICON_PATH = os.path.join('icons', 'icon.ico') -FONT_SIZE = 24 -ACCENT_COLOR = (0, 119, 200, 100) -ROUNDING = 10 -SPACE = 10 -INDENT = 40 -BORDER = 3 - -TEXT = (0, 1) -ACCENT = (5, 18, 19, 20, 15, 16, 35, 37, 34, 23, 22, 25, 26, 27, 28, 29, 10, 11, 12, 30, 31, 32, 49, 50) -PRIMARY = (2, 3, 39) -SECONDARY = (36, 33, 21, 24, 4, 7, 8, 9, 13, 14) -APPLY_ALPHA = (22, 25, 31, 34, 16) -COLOR_TYPE_TO_DPG_ITEM = { - 'TEXT': (0, 1), - 'PRIMARY': (2, 3, 39), - 'SECONDARY': (36, 33, 21, 24, 4, 7, 8, 9, 13, 14), - 'ACCENT': (5, 18, 19, 20, 15, 16, 35, 37, 34, 23, 22, 25, 26, 27, 28, 29, 10, 11, 12, 30, 31, 32, 49, 50)} -COLOR_TO_COLOR_TYPE = {0: 'TEXT', 1: 'TEXT', 2: 'PRIMARY', 3: 'PRIMARY', 39: 'PRIMARY', 36: 'SECONDARY', 21: 'SECONDARY', 24: 'SECONDARY', 4: 'SECONDARY', 7: 'SECONDARY', 8: 'SECONDARY', 9: 'SECONDARY', 13: 'SECONDARY', 33: 'SECONDARY', 14: 'SECONDARY', 35: 'ACCENT', 20: 'ACCENT', 37: 'ACCENT', 22: 'ACCENT', 23: 'ACCENT', 5: 'ACCENT', 25: 'ACCENT', 26: 'ACCENT', 27: 'ACCENT', 28: 'ACCENT', 29: 'ACCENT', 10: 'ACCENT', 30: 'ACCENT', 11: 'ACCENT', 31: 'ACCENT', 12: 'ACCENT', 32: 'ACCENT', 49: 'ACCENT', 34: 'ACCENT', 15: 'ACCENT', 50: 'ACCENT', 16: 'ACCENT', 18: 'ACCENT', 19: 'ACCENT', 1: 'TEXT'} - -BLUE = { - 'enabled': { - 'TEXT': (255, 255, 255, 255), - 'PRIMARY': (37, 37, 38, 255), - 'SECONDARY': (51, 51, 55, 255), - 'ACCENT': (0, 119, 200, 153) - }, - 'disabled': { - 'TEXT': (255, 255, 255, 150), - 'PRIMARY': (37, 37, 38, 255), - 'SECONDARY': (51, 51, 55, 255), - 'ACCENT': (0, 119, 200, 153) - }, -} - -ORANGE = { - 'enabled': { - 'TEXT': (255, 255, 255, 255), - 'PRIMARY': (37, 37, 38, 255), - 'SECONDARY': (51, 51, 55, 255), - 'ACCENT': (251, 133, 0, 153) - }, - 'disabled': { - 'TEXT': (255, 255, 255, 150), - 'PRIMARY': (37, 37, 38, 255), - 'SECONDARY': (51, 51, 55, 255), - 'ACCENT': (251, 133, 0, 153) - }, -} - -THEMES = { - 'blue': BLUE, - 'orange': ORANGE -} \ No newline at end of file diff --git a/dynamic_ui.py b/dynamic_ui.py index 7a8d52922b8c599acaaf534440b7828dba36f841..dc2b64cff4658e9ec959278bad43340a5f61c093 100644 --- a/dynamic_ui.py +++ b/dynamic_ui.py @@ -55,7 +55,7 @@ def construct_config_ui(config_file_path, config): else: construct_config_ui_collapsing_headers(config_file_path, config) -def add_item_to_config_ui(full_path): +def add_item_to_config_ui(full_path, before=None): config_file_path = get_config_file_path() config = get_config() if get_current_ui_type() == TABS_UI: @@ -83,7 +83,10 @@ def add_item_to_config_ui(full_path): path = config_file_path + '.' + '.'.join(namespaces[:level+1]) if not dpg.does_item_exist(path): - dpg.add_collapsing_header(label=namespaces[level], parent=parent, tag=path, indent=INDENT) + if before: + dpg.add_collapsing_header(label=namespaces[level], parent=parent, tag=path, indent=INDENT, before=before) + else: + dpg.add_collapsing_header(label=namespaces[level], parent=parent, tag=path, indent=INDENT) parent = path diff --git a/map_edit_callbacks.py b/map_edit_callbacks.py index 48dbe38e5b2142acbbc3319ec966d9e202a77a96..dfde03cc58dba09f01d087c2da9ed3360c449c0c 100644 --- a/map_edit_callbacks.py +++ b/map_edit_callbacks.py @@ -9,6 +9,43 @@ from map_edit import dump_height_map, load_height_map, update_texture from generator import LCZ from paths import BASE_PATH from utils import show_message, set_main_window_value, get_config, get_config_file_path, delete_item_and_clear_alias, delete_item_children_and_clear_aliases, set_map_window_value, get_map_window_value, get_height_map_file_path +from dynamic_ui import add_item_to_config_ui +from config import CfgVar + +def add_map_to_config_clb(s, a, u): + if not get_config_file_path(): + return + + config = get_config() + + keys = [] + + prev_key = '' + + before = None + + for key in config: + if 'topography' in key: + keys.append(key) + elif 'topography' in prev_key: + before = key.split('.')[0] + break + prev_key = key + + for key in keys: + del config[key] + + set_main_window_value('config', config) + + delete_item_children_and_clear_aliases(f'{get_config_file_path()}.topography') + + config['topography.mode'] = CfgVar(key='mode', value='ascii-mask') + config['topography.filename'] = CfgVar(key='filename', value=get_height_map_file_path()) + + add_item_to_config_ui('topography.mode', before=f'{get_config_file_path()}.{before}') + add_item_to_config_ui('topography.filename', before=dpg.last_item()) + + dump_height_map(get_map_window_value('buildings_map'), get_height_map_file_path()) def delete_emission_point_clb(s, a, u): n = dpg.get_item_user_data('emission_settings_window') @@ -66,7 +103,7 @@ def clear_map(): dpg.set_item_user_data('map_window', settings) def map_window_on_close_clb(s, a, u): - clear_map() + pass def construct_map_layers(w, h): dpg.set_item_width('map_child_window', w) @@ -121,13 +158,15 @@ def map_open_layer_clb(s, a, u): dpg.draw_image(f'map_{layer}_texture', color=LAYER_TO_COLOR[layer], tag=f'map_{layer}_image', pmin=(0, 0), pmax=(w, h), parent=f'map_drawlist') + layer_old = dpg.get_value('layer') + dpg.set_value('layer', layer) update_texture() -def map_open_clb(s, a, u): - height_map_file_path = a['file_path_name'] + dpg.set_value('layer', layer_old) +def open_map(height_map_file_path): if height_map_file_path.endswith('txt'): try: height_map = load_height_map(height_map_file_path) @@ -156,10 +195,14 @@ def map_open_clb(s, a, u): construct_map_layers(w, h) dpg.set_value('layer', 'buildings') + dpg.set_item_label('map_window', f'editing map {height_map_file_path}') update_texture() - dpg.show_item('map_window') +def map_open_clb(s, a, u): + height_map_file_path = a['file_path_name'] + + open_map(height_map_file_path) def generate_map_clb(s, a, u): height_map = LCZ(config_path=os.path.join(BASE_PATH, 'configs', f'{s}.json')).to_height_map(dtype=np.float64) @@ -177,14 +220,14 @@ def generate_map_clb(s, a, u): set_map_window_value('height_map_file_path', 'map.txt') set_map_window_value('max_height', max_height) + dpg.set_item_label('map_window', f'editing map map.txt') + construct_map_layers(w, h) dpg.set_value('layer', 'buildings') update_texture() - dpg.show_item('map_window') - def save_map(file_path): if file_path.endswith('.tif') or file_path.endswith('.tiff'): with TiffWriter(file_path) as tif: diff --git a/map_edit_ui.py b/map_edit_ui.py index ff877d3bf233799749b746ac30b270cc1cb7307c..9163a342f60756f19b4886eca88fd51563a02a62 100644 --- a/map_edit_ui.py +++ b/map_edit_ui.py @@ -69,7 +69,7 @@ def construct_map_edit_window(): dpg.add_menu_item(label=lcz_type, check=False, callback=combo_menu(generate_map_clb), tag=lcz_type) dpg.add_menu_item(label='save', callback=map_save_clb, tag='map_save') dpg.add_menu_item(label='save as', callback=open_map_save_as_dialog_clb, tag='open_map_save_as_dialog') - dpg.add_menu_item(label='add map to config', tag='add_map_to_config') + dpg.add_menu_item(label='add map to config', tag='add_map_to_config', callback=add_map_to_config_clb) with dpg.group(horizontal=True): with dpg.group(width=200, tag='tools'): diff --git a/remote_run.py b/remote_run.py index 76a91d022e822996bfa11d12133dfb1263220430..29fe8d1f936f215f4041a1ce5e4b633da162c526 100644 --- a/remote_run.py +++ b/remote_run.py @@ -81,7 +81,7 @@ class BuildingSession: return '' class RunningSession: - def __init__(self, connection, config_file_path, run_commands, executable_file_path, uuid=None): + def __init__(self, connection, config_file_path, run_commands, executable_file_path, uuid=None, upload_paths=dict()): self.conn = connection if uuid is not None: self.uuid = uuid @@ -93,6 +93,10 @@ class RunningSession: self.conn.exec_command(f'mkdir {self.uuid}; cp {self.executable_file_path} {self.uuid}/nsenx') self.conn.upload(self.config_file_path, f'{self.uuid}/config.txt') + + for path in upload_paths: + self.conn.upload(path, f'{self.uuid}/{upload_paths[path]}') + commands = [f'cd {self.uuid}'] + self.run_commands self.stdout = self.conn.exec_command('; '.join(commands)) diff --git a/remote_run_callbacks.py b/remote_run_callbacks.py index 7356adeae0fc637da2b9cd4c11197a3f9fbd7ad0..3a4f1c331902c7b659b0b4d3d16093afa371c129 100644 --- a/remote_run_callbacks.py +++ b/remote_run_callbacks.py @@ -13,7 +13,7 @@ from remote_run import Connection, BuildingSession, RunningSession from remote_run_constants import * from style_constants import FONT_SIZE from dynamic_ui import construct_model_output_structure_ui -from config import dump_config +from config import dump_config, load_config from style import get_accent_color def update_run_model_window(): @@ -78,6 +78,23 @@ def make_execute_command(): return command +def make_run_commands(running_session_id, executable_file_path, machine, model): + run_commands = [] + + folders = ['chem-forcing', 'chem-init', 'drag-configs', 'drag-forcing', 'meteo-forcing', 'meteo-init'] + prefix = executable_file_path.split('/')[0] + '/nse-gabls1-urban-les' + + for folder in folders: + path = f'{prefix}/{folder}' + run_commands.append(f'cp -r ../{path} {folder}') + + if machine == LOMONOSOV: + run_commands += RUN_COMMANDS[(machine, model)] # + make_run_sh_for_slurm() + ['sbatch ./run.sh'] + else: + run_commands += RUN_COMMANDS[(machine, model)] + [make_execute_command()] + + return run_commands + def update_progress(running_session_id, output): if not re.findall(r'([0-9]+)%', output): progress = dpg.get_value(f'{running_session_id}_progress_bar') @@ -169,12 +186,25 @@ def run_model(): dpg.set_value(f'{running_session_id}_status', 'running') - if machine == LOMONOSOV: - run_commands = RUN_COMMANDS[(machine, model)] # + make_run_sh_for_slurm() + ['sbatch ./run.sh'] + run_commands = make_run_commands(running_session_id, executable_file_path, machine, model) + + upload_paths = dict() + + if get_config_file_path(): + config = get_config() + + if 'topography.filename' in config: + upload_paths = {config['topography.filename'].value: config['topography.filename'].value} else: - run_commands = RUN_COMMANDS[(machine, model)] + [make_execute_command()] + try: + config = load_config(config_file_path) + if 'topography.filename' in config: + upload_paths = {config['topography.filename'].value: config['topography.filename'].value} + except Exception as e: + show_message('bad file format') + return - running_session = RunningSession(conn, config_file_path, run_commands, executable_file_path, running_session_id) + running_session = RunningSession(conn, config_file_path, run_commands, executable_file_path, running_session_id, upload_paths=upload_paths) if machine == LOMONOSOV: output = running_session.output() @@ -223,10 +253,12 @@ def download_output(): dpg.hide_item('model_output_window') dirs, filepaths, running_session_id = dpg.get_item_user_data('model_output_window') - download = [] + download = set() + download_folders = set() for filepath in filepaths: if dpg.get_value(filepath): - download.append(filepath) + download.add(filepath) + download_folders.add('/'.join(filepath.split('/')[:-1])) machine = LABEL_TO_MACHINE[dpg.get_value(f'{running_session_id}_machine')] @@ -235,8 +267,8 @@ def download_output(): dpg.set_value(f'{running_session_id}_status', 'downloading') prefix = os.path.join(download_to_folder) - - construct_folder_structure(dirs, prefix=prefix) + + construct_folder_structure(download_folders, prefix=prefix) conn = get_run_window_value('connections')[machine] diff --git a/remote_run_ui.py b/remote_run_ui.py index fac0c38cb36c2726a5d2d3184e93e0ec2b7340a8..0db618505cf6790fb6f159ad372341a912405751 100644 --- a/remote_run_ui.py +++ b/remote_run_ui.py @@ -7,10 +7,24 @@ from remote_run_callbacks import * from remote_run import * def construct_remote_run_window(): - user_data = { - 'connections': dict(), - 'builds': dict() - } + if 'USE_BUILDS' in os.environ: + conn = { + LAB: Connection(LAB_HOST, os.getenv('SERVER_LOGIN', ''), os.getenv('SERVER_PASS', '')), + # LOMONOSOV: Connection(LOMONOSOV_HOST, os.getenv('LOMONOSOV_LOGIN'), id_rsa_path=os.getenv('ID_RSA_PATH', '')) + } + builds = { + (LAB, URBAN_LES): 'prebuild/code/nsenx', + # (LOMONOSOV, URBAN_LES): '6e094bc0-a5f6-4474-8c71-a2e3a90d8e85/build/nse-gabls1-urban-les' + } + user_data = { + 'connections': conn, + 'builds': builds + } + else: + user_data = { + 'connections': dict(), + 'builds': dict() + } with dpg.window( label='remote run',