Loading src/ips_fastran_gui/app/models/ips_fastran.py +16 −7 Original line number Diff line number Diff line Loading @@ -3,8 +3,7 @@ import logging import zipfile from io import BytesIO from pathlib import Path from typing import Tuple from typing import List, Tuple from nova.galaxy import Dataset, Parameters, Tool from nova.galaxy.interfaces import BasicTool Loading Loading @@ -38,16 +37,26 @@ class IPSFastranTool(BasicTool): tool_params.add_input(name="__job_resource|time", value=self.model.resource_params.time_limit) if self.model.resource_params.compute_resource == ComputeOptions.perlmutter: tool_params.add_input(name="__job_resource|remote_resource_cloud_nersc", value="nersc") self.tool = Tool(id="fusion_ips_fastran") self.tool = Tool(id="fusion_ips_fastran_test") return self.tool, tool_params def get_output_path(self) -> Path: def get_output_paths(self) -> List[str]: outputs = self.tool.get_results() dataset = outputs.get_dataset("output") collection = outputs.get_collection("outputs") elements = collection.get_content() dataset.download("output.nc") return Path("output.nc") paths = [] for element in elements: dataset = Dataset() dataset.id = element["id"] dataset.store = collection.store path = element["element_identifier"] dataset.download(path) paths.append(path) return paths def get_results(self, tool: Tool) -> bytes: outputs = tool.get_results() Loading src/ips_fastran_gui/app/models/main_model.py +23 −1 Original line number Diff line number Diff line """Module for the main model.""" import json from enum import Enum from pathlib import Path from typing import Any, Dict, List from pydantic import BaseModel, Field with open(Path(__file__).parent / "plot.json") as file_pointer: PLOT_JSON = json.load(file_pointer) class Config(BaseModel): """ Loading @@ -18,6 +24,8 @@ class Config(BaseModel): shot_number: str = Field(default="000001", title="Shot Number") time_id: str = Field(default="00001", title="Time ID") file_contents: str = Field(default="") result_file: str = Field(default="", title="File to Plot") result_files: List[str] = Field(default=[]) class ComputeOptions(str, Enum): Loading @@ -36,15 +44,29 @@ class ResourceParameters(BaseModel): time_limit: int = Field(default=10, ge=1, le=10080, title="Time Limit (minutes)") class PlotJSON(BaseModel): """Contains the plot.json file contents.""" input_params: Dict[Any, Any] = Field(default=PLOT_JSON) class MainModel: """Main model class.""" def __init__(self) -> None: self.config = Config() self.plot_json = PlotJSON() self.resource_params = ResourceParameters() def get_config_file(self) -> str: return self.config.file_contents def set_from_json(self, json_data: str) -> None: def set_config(self, json_data: str) -> None: self.config.file_contents = json_data def set_plot(self, json_data: str) -> None: self.plot_json = PlotJSON.model_validate_json(json_data) def set_results(self, paths: List[str]) -> None: self.config.result_files = paths self.config.result_file = paths[0] src/ips_fastran_gui/app/models/plotter.py +6 −4 Original line number Diff line number Diff line """Fastran Plotter.""" import json import os from pathlib import Path from typing import Any, Dict # default plot preferences import matplotlib Loading @@ -17,12 +17,14 @@ matplotlib.rcParams["figure.max_open_warning"] = 0 class PlotFastran: """Fastran Plotter.""" def __init__(self, figure: Figure) -> None: def __init__(self, figure: Figure, input_params: Dict[str, Any]) -> None: self.figure = figure with open(Path(os.path.dirname(__file__)) / "plot.json") as plot_json: self.input_params = json.load(plot_json) self.input_params = input_params def load_fastran(self, fn_ncfile: Path) -> None: print(fn_ncfile) # TODO: remove fn_ncfile = Path("test.nc") # TODO: remove self.fastran = netCDF4.Dataset(fn_ncfile, "r", format="NETCDF4") self.basename = os.path.basename(fn_ncfile) Loading src/ips_fastran_gui/app/view_models/execution.py +4 −2 Original line number Diff line number Diff line Loading @@ -36,8 +36,10 @@ class ExecutionViewModel: async def on_progress(self, _sender: Any, state: WorkState, details: Dict[str, Any]) -> None: if state == WorkState.FINISHED: path = self.tool.get_output_path() self.completion_signal.send(output_path=path) paths = self.tool.get_output_paths() self.model.set_results(paths) self.completion_signal.send() def store_factory(self) -> str: return "ips_fastran" src/ips_fastran_gui/app/view_models/main_view_model.py +19 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ class MainViewModel: self.resource_params_bind = binding.new_bind( self.model.resource_params, callback_after_update=self.on_change_resource_params ) self.plot_json_bind = binding.new_bind() self.view_state_bind = binding.new_bind(self.view_state) # Signals to process events from other view models Loading @@ -57,6 +58,7 @@ class MainViewModel: def init_view(self) -> None: self.config_bind.update_in_view(self.model.config) self.plot_json_bind.update_in_view(self.model.plot_json.model_dump_json(indent=2)) def on_change_config_file(self, json_data: str) -> None: # Monaco fires input events with internal data that need to be ignored. Loading @@ -65,7 +67,7 @@ class MainViewModel: self.view_state.errors = [] try: self.model.set_from_json(json_data) self.model.set_config(json_data) except ValidationError as e: for error in e.errors(): msg = "" Loading @@ -82,26 +84,37 @@ class MainViewModel: # You can take actions in Python when specific fields are changed here. pass def on_change_plot(self, json_data: str) -> None: if "_vts" in json_data: return try: self.model.set_plot(json_data) except ValidationError: pass def on_change_resource_params(self, results: Dict[str, Any]) -> None: if results["updated"]: # You can take actions in Python when specific fields are changed here. pass def on_completion(self, _sender: Any, output_path: Path) -> None: self.update_figure(output_path) def on_completion(self, _sender: Any) -> None: self.config_bind.update_in_view(self.model.config) self.view_state.results_disabled = False self.view_state_bind.update_in_view(self.view_state) self.update_figure() async def on_tool_command(self, _sender: Any, command: str) -> None: if command == ToolCommand.START: self.view_state.results_disabled = True self.view_state.active_tab = 2 self.view_state_bind.update_in_view(self.view_state) def update_figure(self, output_path: Path) -> None: plotter = PlotFastran(self.figure) plotter.load_fastran(output_path) def update_figure(self) -> None: plotter = PlotFastran(self.figure, self.model.plot_json.input_params) plotter.load_fastran(Path(self.model.config.result_file)) plotter.set_page() self.figure_bind.update_in_view(None) Loading
src/ips_fastran_gui/app/models/ips_fastran.py +16 −7 Original line number Diff line number Diff line Loading @@ -3,8 +3,7 @@ import logging import zipfile from io import BytesIO from pathlib import Path from typing import Tuple from typing import List, Tuple from nova.galaxy import Dataset, Parameters, Tool from nova.galaxy.interfaces import BasicTool Loading Loading @@ -38,16 +37,26 @@ class IPSFastranTool(BasicTool): tool_params.add_input(name="__job_resource|time", value=self.model.resource_params.time_limit) if self.model.resource_params.compute_resource == ComputeOptions.perlmutter: tool_params.add_input(name="__job_resource|remote_resource_cloud_nersc", value="nersc") self.tool = Tool(id="fusion_ips_fastran") self.tool = Tool(id="fusion_ips_fastran_test") return self.tool, tool_params def get_output_path(self) -> Path: def get_output_paths(self) -> List[str]: outputs = self.tool.get_results() dataset = outputs.get_dataset("output") collection = outputs.get_collection("outputs") elements = collection.get_content() dataset.download("output.nc") return Path("output.nc") paths = [] for element in elements: dataset = Dataset() dataset.id = element["id"] dataset.store = collection.store path = element["element_identifier"] dataset.download(path) paths.append(path) return paths def get_results(self, tool: Tool) -> bytes: outputs = tool.get_results() Loading
src/ips_fastran_gui/app/models/main_model.py +23 −1 Original line number Diff line number Diff line """Module for the main model.""" import json from enum import Enum from pathlib import Path from typing import Any, Dict, List from pydantic import BaseModel, Field with open(Path(__file__).parent / "plot.json") as file_pointer: PLOT_JSON = json.load(file_pointer) class Config(BaseModel): """ Loading @@ -18,6 +24,8 @@ class Config(BaseModel): shot_number: str = Field(default="000001", title="Shot Number") time_id: str = Field(default="00001", title="Time ID") file_contents: str = Field(default="") result_file: str = Field(default="", title="File to Plot") result_files: List[str] = Field(default=[]) class ComputeOptions(str, Enum): Loading @@ -36,15 +44,29 @@ class ResourceParameters(BaseModel): time_limit: int = Field(default=10, ge=1, le=10080, title="Time Limit (minutes)") class PlotJSON(BaseModel): """Contains the plot.json file contents.""" input_params: Dict[Any, Any] = Field(default=PLOT_JSON) class MainModel: """Main model class.""" def __init__(self) -> None: self.config = Config() self.plot_json = PlotJSON() self.resource_params = ResourceParameters() def get_config_file(self) -> str: return self.config.file_contents def set_from_json(self, json_data: str) -> None: def set_config(self, json_data: str) -> None: self.config.file_contents = json_data def set_plot(self, json_data: str) -> None: self.plot_json = PlotJSON.model_validate_json(json_data) def set_results(self, paths: List[str]) -> None: self.config.result_files = paths self.config.result_file = paths[0]
src/ips_fastran_gui/app/models/plotter.py +6 −4 Original line number Diff line number Diff line """Fastran Plotter.""" import json import os from pathlib import Path from typing import Any, Dict # default plot preferences import matplotlib Loading @@ -17,12 +17,14 @@ matplotlib.rcParams["figure.max_open_warning"] = 0 class PlotFastran: """Fastran Plotter.""" def __init__(self, figure: Figure) -> None: def __init__(self, figure: Figure, input_params: Dict[str, Any]) -> None: self.figure = figure with open(Path(os.path.dirname(__file__)) / "plot.json") as plot_json: self.input_params = json.load(plot_json) self.input_params = input_params def load_fastran(self, fn_ncfile: Path) -> None: print(fn_ncfile) # TODO: remove fn_ncfile = Path("test.nc") # TODO: remove self.fastran = netCDF4.Dataset(fn_ncfile, "r", format="NETCDF4") self.basename = os.path.basename(fn_ncfile) Loading
src/ips_fastran_gui/app/view_models/execution.py +4 −2 Original line number Diff line number Diff line Loading @@ -36,8 +36,10 @@ class ExecutionViewModel: async def on_progress(self, _sender: Any, state: WorkState, details: Dict[str, Any]) -> None: if state == WorkState.FINISHED: path = self.tool.get_output_path() self.completion_signal.send(output_path=path) paths = self.tool.get_output_paths() self.model.set_results(paths) self.completion_signal.send() def store_factory(self) -> str: return "ips_fastran"
src/ips_fastran_gui/app/view_models/main_view_model.py +19 −6 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ class MainViewModel: self.resource_params_bind = binding.new_bind( self.model.resource_params, callback_after_update=self.on_change_resource_params ) self.plot_json_bind = binding.new_bind() self.view_state_bind = binding.new_bind(self.view_state) # Signals to process events from other view models Loading @@ -57,6 +58,7 @@ class MainViewModel: def init_view(self) -> None: self.config_bind.update_in_view(self.model.config) self.plot_json_bind.update_in_view(self.model.plot_json.model_dump_json(indent=2)) def on_change_config_file(self, json_data: str) -> None: # Monaco fires input events with internal data that need to be ignored. Loading @@ -65,7 +67,7 @@ class MainViewModel: self.view_state.errors = [] try: self.model.set_from_json(json_data) self.model.set_config(json_data) except ValidationError as e: for error in e.errors(): msg = "" Loading @@ -82,26 +84,37 @@ class MainViewModel: # You can take actions in Python when specific fields are changed here. pass def on_change_plot(self, json_data: str) -> None: if "_vts" in json_data: return try: self.model.set_plot(json_data) except ValidationError: pass def on_change_resource_params(self, results: Dict[str, Any]) -> None: if results["updated"]: # You can take actions in Python when specific fields are changed here. pass def on_completion(self, _sender: Any, output_path: Path) -> None: self.update_figure(output_path) def on_completion(self, _sender: Any) -> None: self.config_bind.update_in_view(self.model.config) self.view_state.results_disabled = False self.view_state_bind.update_in_view(self.view_state) self.update_figure() async def on_tool_command(self, _sender: Any, command: str) -> None: if command == ToolCommand.START: self.view_state.results_disabled = True self.view_state.active_tab = 2 self.view_state_bind.update_in_view(self.view_state) def update_figure(self, output_path: Path) -> None: plotter = PlotFastran(self.figure) plotter.load_fastran(output_path) def update_figure(self) -> None: plotter = PlotFastran(self.figure, self.model.plot_json.input_params) plotter.load_fastran(Path(self.model.config.result_file)) plotter.set_page() self.figure_bind.update_in_view(None)