import json from datetime import datetime from math import pi, sqrt import pytz import app.services.simulation as simulation from app.algorithms.api_ex.run_simulation import ( run_simulation_ex, from_clock_to_seconds_2, ) from app.native.api.project import copy_project from app.services.epanet.epanet import Output from app.services.scheme_management import store_scheme_info from app.services.tjnetwork import * ############################################################ # burst analysis 01 ############################################################ def convert_to_local_unit(proj: str, emitters: float) -> float: open_project(proj) proj_opt = get_option(proj) str_unit = proj_opt.get("UNITS") if str_unit == "CMH": return emitters * 3.6 elif str_unit == "LPS": return emitters elif str_unit == "CMS": return emitters / 1000.0 elif str_unit == "MGD": return emitters * 0.0438126 # Unknown unit: log and return original value print(str_unit) return emitters def burst_analysis( name: str, modify_pattern_start_time: str, burst_ID: list | str = None, burst_size: list | float | int = None, modify_total_duration: int = 900, modify_fixed_pump_pattern: dict[str, list] = None, modify_variable_pump_pattern: dict[str, list] = None, modify_valve_opening: dict[str, float] = None, scheme_name: str = None, ) -> None: """ 爆管模拟 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param burst_ID: 爆管管道的ID,选取的是管道,单独传入一个爆管管道,可以是str或list,传入多个爆管管道是用list :param burst_size: 爆管管道破裂的孔口面积,和burst_ID列表各位置的ID对应,以cm*cm计算 :param modify_total_duration: 模拟总历时,秒 :param modify_fixed_pump_pattern: dict中包含多个水泵模式,str为工频水泵的id,list为修改后的pattern :param modify_variable_pump_pattern: dict中包含多个水泵模式,str为变频水泵的id,list为修改后的pattern :param modify_valve_opening: dict中包含多个阀门开启度,str为阀门的id,float为修改后的阀门开启度 :param scheme_name: 方案名称 :return: """ scheme_detail: dict = { "burst_ID": burst_ID, "burst_size": burst_size, "modify_total_duration": modify_total_duration, "modify_fixed_pump_pattern": modify_fixed_pump_pattern, "modify_variable_pump_pattern": modify_variable_pump_pattern, "modify_valve_opening": modify_valve_opening, } print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"burst_Anal_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) simulation.run_simulation( name=new_name, simulation_type="manually_temporary", modify_pattern_start_time=modify_pattern_start_time, ) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) ##step 1 set the emitter coefficient of end node of busrt pipe if isinstance(burst_ID, list): if (burst_size is not None) and (type(burst_size) is not list): return json.dumps("Type mismatch.") # 转化为列表形式 elif isinstance(burst_ID, str): burst_ID = [burst_ID] if burst_size is not None: if isinstance(burst_size, float) or isinstance(burst_size, int): burst_size = [burst_size] else: return json.dumps("Type mismatch.") else: return json.dumps("Type mismatch.") if burst_size is None: burst_size = [-1] * len(burst_ID) elif len(burst_size) < len(burst_ID): burst_size += [-1] * (len(burst_ID) - len(burst_size)) elif len(burst_size) > len(burst_ID): # burst_size = burst_size[:len(burst_ID)] return json.dumps("Length mismatch.") for burst_ID_, burst_size_ in zip(burst_ID, burst_size): pipe = get_pipe(new_name, burst_ID_) str_start_node = pipe["node1"] str_end_node = pipe["node2"] d_pipe = pipe["diameter"] / 1000.0 if burst_size_ <= 0: burst_size_ = 3.14 * d_pipe * d_pipe / 4 / 8 else: burst_size_ = burst_size_ / 10000 emitter_coeff = ( 0.65 * burst_size_ * sqrt(19.6) * 1000 ) # 1/8开口面积作为coeff,单位 L/S emitter_coeff = convert_to_local_unit(new_name, emitter_coeff) emitter_node = "" if is_junction(new_name, str_end_node): emitter_node = str_end_node elif is_junction(new_name, str_start_node): emitter_node = str_start_node old_emitter = get_emitter(new_name, emitter_node) if old_emitter != None: old_emitter["coefficient"] = emitter_coeff # 爆管的emitter coefficient设置 else: old_emitter = {"junction": emitter_node, "coefficient": emitter_coeff} new_emitter = ChangeSet() new_emitter.append(old_emitter) set_emitter(new_name, new_emitter) # step 2. run simulation # 涉及关阀计算,可能导致关阀后仍有流量,改为压力驱动PDA options = get_option(new_name) options["DEMAND MODEL"] = OPTION_DEMAND_MODEL_PDA options["REQUIRED PRESSURE"] = "10.0000" cs_options = ChangeSet() cs_options.append(options) set_option(new_name, cs_options) # valve_control = None # if modify_valve_opening is not None: # valve_control = {} # for valve in modify_valve_opening: # valve_control[valve] = {'status': 'CLOSED'} # result = run_simulation_ex(new_name,'realtime', modify_pattern_start_time, # end_datetime=modify_pattern_start_time, # modify_total_duration=modify_total_duration, # modify_pump_pattern=modify_pump_pattern, # valve_control=valve_control, # downloading_prohibition=True) simulation.run_simulation( name=new_name, simulation_type="extended", modify_pattern_start_time=modify_pattern_start_time, modify_total_duration=modify_total_duration, modify_fixed_pump_pattern=modify_fixed_pump_pattern, modify_variable_pump_pattern=modify_variable_pump_pattern, modify_valve_opening=modify_valve_opening, scheme_type="burst_analysis", scheme_name=scheme_name, ) # step 3. restore the base model status # execute_undo(name) #有疑惑 if is_project_open(new_name): close_project(new_name) delete_project(new_name) # 存储方案信息到 PG 数据库 store_scheme_info( name=name, scheme_name=scheme_name, scheme_type="burst_analysis", username="admin", scheme_start_time=modify_pattern_start_time, scheme_detail=scheme_detail, ) ############################################################ # valve closing analysis 02 ############################################################ def valve_close_analysis( name: str, modify_pattern_start_time: str, modify_total_duration: int = 900, modify_valve_opening: dict[str, float] = None, scheme_name: str = None, ) -> None: """ 关阀模拟 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param modify_total_duration: 模拟总历时,秒 :param modify_valve_opening: dict中包含多个阀门开启度,str为阀门的id,float为修改后的阀门开启度 :param scheme_name: 方案名称 :return: """ print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"valve_close_Anal_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) # step 1. change the valves status to 'closed' # for valve in valves: # if not is_valve(new_name,valve): # result='ID:{}is not a valve type'.format(valve) # return result # cs=ChangeSet() # status=get_status(new_name,valve) # status['status']='CLOSED' # cs.append(status) # set_status(new_name,cs) # step 2. run simulation # 涉及关阀计算,可能导致关阀后仍有流量,改为压力驱动PDA options = get_option(new_name) options["DEMAND MODEL"] = OPTION_DEMAND_MODEL_PDA options["REQUIRED PRESSURE"] = "20.0000" cs_options = ChangeSet() cs_options.append(options) set_option(new_name, cs_options) # result = run_simulation_ex(new_name,'realtime', modify_pattern_start_time, modify_pattern_start_time, modify_total_duration, # downloading_prohibition=True) simulation.run_simulation( name=new_name, simulation_type="extended", modify_pattern_start_time=modify_pattern_start_time, modify_total_duration=modify_total_duration, modify_valve_opening=modify_valve_opening, scheme_type="valve_close_Analysis", scheme_name=scheme_name, ) # step 3. restore the base model # for valve in valves: # execute_undo(name) if is_project_open(new_name): close_project(new_name) delete_project(new_name) # return result ############################################################ # flushing analysis 03 # Pipe_Flushing_Analysis(prj_name,date_time, Valve_id_list, Drainage_Node_Id, Flushing_flow[opt], Flushing_duration[opt])->out_file:string ############################################################ def flushing_analysis( name: str, modify_pattern_start_time: str, modify_total_duration: int = 900, modify_valve_opening: dict[str, float] = None, drainage_node_ID: str = None, flushing_flow: float = 0, scheme_name: str = None, ) -> None: """ 管道冲洗模拟 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param modify_total_duration: 模拟总历时,秒 :param modify_valve_opening: dict中包含多个阀门开启度,str为阀门的id,float为修改后的阀门开启度 :param drainage_node_ID: 冲洗排放口所在节点ID :param flushing_flow: 冲洗水量,传入参数单位为m3/h :param scheme_name: 方案名称 :return: """ scheme_detail: dict = { "duration": modify_total_duration, "valve_opening": modify_valve_opening, "drainage_node_ID": drainage_node_ID, "flushing_flow": flushing_flow, } print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"flushing_Anal_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) # if is_project_open(name): # close_project(name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) if not is_junction(new_name, drainage_node_ID): return "Wrong Drainage node type" # step 1. change the valves status to 'closed' # for valve, valve_k in zip(valves, valves_k): # cs=ChangeSet() # status=get_status(new_name,valve) # # status['status']='CLOSED' # if valve_k == 0: # status['status'] = 'CLOSED' # elif valve_k < 1: # status['status'] = 'OPEN' # status['setting'] = 0.1036 * pow(valve_k, -3.105) # cs.append(status) # set_status(new_name,cs) options = get_option(new_name) units = options["UNITS"] # step 2. set the emitter coefficient of drainage node or add flush flow to the drainage node emitter_demand = get_demand(new_name, drainage_node_ID) cs = ChangeSet() if flushing_flow > 0: for r in emitter_demand["demands"]: if units == "LPS": r["demand"] += flushing_flow / 3.6 elif units == "CMH": r["demand"] += flushing_flow cs.append(emitter_demand) set_demand(new_name, cs) else: pipes = get_node_links(new_name, drainage_node_ID) flush_diameter = 50 for pipe in pipes: d = get_pipe(new_name, pipe)["diameter"] if flush_diameter < d: flush_diameter = d flush_diameter /= 1000 emitter_coeff = ( 0.65 * 3.14 * (flush_diameter * flush_diameter / 4) * sqrt(19.6) * 1000 ) # 全开口面积作为coeff old_emitter = get_emitter(new_name, drainage_node_ID) if old_emitter != None: old_emitter["coefficient"] = emitter_coeff # 爆管的emitter coefficient设置 else: old_emitter = {"junction": drainage_node_ID, "coefficient": emitter_coeff} new_emitter = ChangeSet() new_emitter.append(old_emitter) set_emitter(new_name, new_emitter) # step 3. run simulation # 涉及关阀计算,可能导致关阀后仍有流量,改为压力驱动PDA options = get_option(new_name) options["DEMAND MODEL"] = OPTION_DEMAND_MODEL_PDA options["REQUIRED PRESSURE"] = "20.0000" cs_options = ChangeSet() cs_options.append(options) set_option(new_name, cs_options) # result = run_simulation_ex(new_name,'realtime', modify_pattern_start_time, modify_pattern_start_time, modify_total_duration, # downloading_prohibition=True) simulation.run_simulation( name=new_name, simulation_type="extended", modify_pattern_start_time=modify_pattern_start_time, modify_total_duration=modify_total_duration, modify_valve_opening=modify_valve_opening, scheme_type="flushing_analysis", scheme_name=scheme_name, ) # step 4. restore the base model if is_project_open(new_name): close_project(new_name) delete_project(new_name) # return result # 存储方案信息到 PG 数据库 store_scheme_info( name=name, scheme_name=scheme_name, scheme_type="flushing_analysis", username="admin", scheme_start_time=modify_pattern_start_time, scheme_detail=scheme_detail, ) ############################################################ # Contaminant simulation 04 # ############################################################ def contaminant_simulation( name: str, modify_pattern_start_time: str, # 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' modify_total_duration: int, # 模拟总历时,秒 source: str, # 污染源节点ID concentration: float, # 污染源浓度,单位mg/L scheme_name: str = None, source_pattern: str = None, # 污染源时间变化模式名称 ) -> None: """ 污染模拟 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param modify_total_duration: 模拟总历时,秒 :param source: 污染源所在的节点ID :param concentration: 污染源位置处的浓度,单位mg/L。默认的污染模拟setting为SOURCE_TYPE_CONCEN(改为SOURCE_TYPE_SETPOINT) :param source_pattern: 污染源的时间变化模式,若不传入则默认以恒定浓度持续模拟,时间长度等于duration; 若传入,则格式为{1.0,0.5,1.1}等系数列表pattern_step模拟等于模型的hydraulic time step :param scheme_name: 方案名称 :return: """ scheme_detail: dict = { "source": source, "concentration": concentration, "duration": modify_total_duration, "pattern": source_pattern, } print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"contaminant_Sim_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) # if is_project_open(name): # close_project(name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) dic_time = get_time(new_name) dic_time["QUALITY TIMESTEP"] = "0:05:00" cs = ChangeSet() cs.operations.append(dic_time) set_time(new_name, cs) # set QUALITY TIMESTEP time_option = get_time(new_name) hydraulic_step = time_option["HYDRAULIC TIMESTEP"] secs = from_clock_to_seconds_2(hydraulic_step) operation_step = 0 # step 1. set duration if modify_total_duration == None: modify_total_duration = secs # step 2. set pattern if source_pattern != None: pt = get_pattern(new_name, source_pattern) if pt == None: str_response = str("cant find source_pattern") return str_response else: cs_pattern = ChangeSet() pt = {} factors = [] tmp_duration = modify_total_duration while tmp_duration > 0: factors.append(1.0) tmp_duration = tmp_duration - secs pt["id"] = "contam_pt" pt["factors"] = factors cs_pattern.append(pt) add_pattern(new_name, cs_pattern) operation_step += 1 # step 3. set source/initial quality # source quality cs_source = ChangeSet() source_schema = { "node": source, "s_type": SOURCE_TYPE_SETPOINT, "strength": concentration, "pattern": pt["id"], } cs_source.append(source_schema) source_node = get_source(new_name, source) if len(source_node) == 0: add_source(new_name, cs_source) else: set_source(new_name, cs_source) dict_demand = get_demand(new_name, source) for demands in dict_demand["demands"]: dict_demand["demands"][dict_demand["demands"].index(demands)]["demand"] = -1 dict_demand["demands"][dict_demand["demands"].index(demands)]["pattern"] = None cs = ChangeSet() cs.append(dict_demand) set_demand(new_name, cs) # set inflow node # # initial quality # dict_quality = get_quality(new_name, source) # dict_quality['quality'] = concentration # cs = ChangeSet() # cs.append(dict_quality) # set_quality(new_name, cs) operation_step += 1 # step 4 set option of quality to chemical opt = get_option(new_name) opt["QUALITY"] = OPTION_QUALITY_CHEMICAL cs_option = ChangeSet() cs_option.append(opt) set_option(new_name, cs_option) operation_step += 1 # step 5. run simulation # result = run_simulation_ex(new_name,'realtime', modify_pattern_start_time, modify_pattern_start_time, modify_total_duration, # downloading_prohibition=True) simulation.run_simulation( name=new_name, simulation_type="extended", modify_pattern_start_time=modify_pattern_start_time, modify_total_duration=modify_total_duration, scheme_type="contaminant_analysis", scheme_name=scheme_name, ) # for i in range(1,operation_step): # execute_undo(name) if is_project_open(new_name): close_project(new_name) delete_project(new_name) # 存储方案信息到 PG 数据库 store_scheme_info( name=name, scheme_name=scheme_name, scheme_type="contaminant_analysis", username="admin", scheme_start_time=modify_pattern_start_time, scheme_detail=scheme_detail, ) ############################################################ # age analysis 05 ***水龄模拟目前还没和实时模拟打通,不确定是否需要,先不要使用*** ############################################################ def age_analysis( name: str, modify_pattern_start_time: str, modify_total_duration: int = 900 ) -> None: """ 水龄模拟 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param modify_total_duration: 模拟总历时,秒 :return: """ print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"age_Anal_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) # if is_project_open(name): # close_project(name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) # step 1. run simulation result = run_simulation_ex( new_name, "realtime", modify_pattern_start_time, modify_total_duration, downloading_prohibition=True, ) # step 2. restore the base model status # execute_undo(name) #有疑惑 if is_project_open(new_name): close_project(new_name) delete_project(new_name) output = Output("./temp/{}.db.out".format(new_name)) # element_name = output.element_name() # node_name = element_name['nodes'] # link_name = element_name['links'] nodes_age = [] node_result = output.node_results() for node in node_result: nodes_age.append(node["result"][-1]["quality"]) links_age = [] link_result = output.link_results() for link in link_result: links_age.append(link["result"][-1]["quality"]) age_result = {"nodes": nodes_age, "links": links_age} # age_result = {'nodes': nodes_age, 'links': links_age, 'nodeIDs': node_name, 'linkIDs': link_name} return json.dumps(age_result) ############################################################ # pressure regulation 06 ############################################################ def pressure_regulation( name: str, modify_pattern_start_time: str, modify_total_duration: int = 900, modify_tank_initial_level: dict[str, float] = None, modify_fixed_pump_pattern: dict[str, list] = None, modify_variable_pump_pattern: dict[str, list] = None, scheme_name: str = None, ) -> None: """ 区域调压模拟,用来模拟未来15分钟内,开关水泵对区域压力的影响 :param name: 模型名称,数据库中对应的名字 :param modify_pattern_start_time: 模拟开始时间,格式为'2024-11-25T09:00:00+08:00' :param modify_total_duration: 模拟总历时,秒 :param modify_tank_initial_level: dict中包含多个水塔,str为水塔的id,float为修改后的initial_level :param modify_fixed_pump_pattern: dict中包含多个水泵模式,str为工频水泵的id,list为修改后的pattern :param modify_variable_pump_pattern: dict中包含多个水泵模式,str为变频水泵的id,list为修改后的pattern :param scheme_name: 模拟方案名称 :return: """ print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Analysis." ) new_name = f"pressure_regulation_{name}" if have_project(new_name): if is_project_open(new_name): close_project(new_name) delete_project(new_name) # if is_project_open(name): # close_project(name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Copying Database." ) # CopyProjectEx()(name, new_name, # ['operation', 'current_operation', 'restore_operation', 'batch_operation', 'operation_table']) copy_project(name + "_template", new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Start Opening Database." ) open_project(new_name) print( datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") + " -- Database Loading OK." ) # 全部关泵后,压力计算不合理,改为压力驱动PDA options = get_option(new_name) options["DEMAND MODEL"] = OPTION_DEMAND_MODEL_PDA options["REQUIRED PRESSURE"] = "15.0000" cs_options = ChangeSet() cs_options.append(options) set_option(new_name, cs_options) # result = run_simulation_ex(name=new_name, # simulation_type='realtime', # start_datetime=start_datetime, # duration=900, # pump_control=pump_control, # tank_initial_level_control=tank_initial_level_control, # downloading_prohibition=True) simulation.run_simulation( name=new_name, simulation_type="extended", modify_pattern_start_time=modify_pattern_start_time, modify_total_duration=modify_total_duration, modify_tank_initial_level=modify_tank_initial_level, modify_fixed_pump_pattern=modify_fixed_pump_pattern, modify_variable_pump_pattern=modify_variable_pump_pattern, scheme_type="pressure_regulation", scheme_name=scheme_name, ) if is_project_open(new_name): close_project(new_name) delete_project(new_name) # return result