diff --git a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.cxx b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.cxx index 5d0e1bb6f..51fbd9570 100644 --- a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.cxx +++ b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.cxx @@ -150,6 +150,10 @@ bool sv4guiSimulationPythonConvert1d::ConvertResults(const std::string outputDir mb.setDetailedText(QString(sResult.c_str())); mb.setDefaultButton(QMessageBox::Ok); mb.exec(); + } else { + QString rmsg = "1D solver files have been successfully converted.\n"; + MITK_INFO << msg << rmsg; + QMessageBox::information(NULL, sv4guiSimulationView1d::MsgTitle, rmsg); } } diff --git a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.h b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.h index 680b403a5..17c3ce1bb 100644 --- a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.h +++ b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationPythonConvert1d.h @@ -59,6 +59,7 @@ class sv4guiSimulationPythonConvert1dParamNames sv4guiSimulationPythonConvert1dParamNames() { allNames.insert(OUTPUT_DIRECTORY); } + const std::string ALL_SEGMENTS = "all_segments"; const std::string DATA_NAMES = "data_names"; const std::string OUTPUT_DIRECTORY = "output_directory"; const std::string OUTPUT_FILE_NAME = "output_file_name"; diff --git a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.cxx b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.cxx index 3ace8c3ec..c7dd7a0fb 100644 --- a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.cxx +++ b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.cxx @@ -263,6 +263,22 @@ const std::vector sv4guiSimulationView1d::SegmentExportType::types = sv4guiSimulationView1d::SegmentExportType::OUTLET }; +// Set export data names. +// +const QString sv4guiSimulationView1d::DataExportName::AREA = "area"; +const QString sv4guiSimulationView1d::DataExportName::FLOW = "flow"; +const QString sv4guiSimulationView1d::DataExportName::PRESSURE = "pressure"; +const QString sv4guiSimulationView1d::DataExportName::WSS = "wss"; +const QString sv4guiSimulationView1d::DataExportName::RE = "Re"; +const std::vector sv4guiSimulationView1d::DataExportName::names = +{ + sv4guiSimulationView1d::DataExportName::AREA, + sv4guiSimulationView1d::DataExportName::FLOW, + sv4guiSimulationView1d::DataExportName::PRESSURE, + sv4guiSimulationView1d::DataExportName::RE, + sv4guiSimulationView1d::DataExportName::WSS +}; + //------------------------ // sv4guiSimulationView1d @@ -496,7 +512,7 @@ void sv4guiSimulationView1d::CreateQtPartControl( QWidget *parent ) ui->CreateSimulationFilesButton->setEnabled(false); ui->RunSimulationPushButton->setEnabled(false); - // Convert Results. + // Convert Results toolbox tab. // connect(ui->SegmentExportComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectSegmentExportType(int))); for (auto const& type : SegmentExportType::types) { @@ -504,6 +520,14 @@ void sv4guiSimulationView1d::CreateQtPartControl( QWidget *parent ) } ui->SegmentExportComboBox->setCurrentIndex(1); + for (auto const& name : DataExportName::names) { + ui->DataExportListWidget->addItem(name); + //QListWidgetItem *listItem = new QListWidgetItem(name, listWidget); + //listItem->setCheckState(Qt::Unchecked); + //ui->DataExportListWidget->addItem(listItem); + } + ui->DataExportListWidget->setSelectionMode(QListWidget::MultiSelection); + connect(ui->toolButtonResultDir, SIGNAL(clicked()), this, SLOT(SetResultDir()) ); connect(ui->btnExportResults, SIGNAL(clicked()), this, SLOT(ExportResults()) ); @@ -4433,16 +4457,32 @@ void sv4guiSimulationView1d::ExportResults() pythonInterface.AddParameter(params.RESULTS_DIRECTORY, resultDir.toStdString()); pythonInterface.AddParameter(params.SOLVER_FILE_NAME, "solver.in"); - pythonInterface.AddParameter(params.OUTPUT_DIRECTORY, exportDir.toStdString()); pythonInterface.AddParameter(params.OUTPUT_FILE_NAME, jobName.toStdString()); - pythonInterface.AddParameter(params.DATA_NAMES, "flow"); - - pythonInterface.AddParameter(params.TIME_RANGE, "0.0,0.8"); - - // Convert resuls for all outlet segments. - pythonInterface.AddParameter(params.OUTLET_SEGMENTS, "true"); + // Set the data names to convert. + // + std::string dataNames; + auto selectedItems = ui->DataExportListWidget->selectedItems(); + for (auto const& item : selectedItems) { + auto dataName = item->text().toStdString(); + MITK_INFO << msg << "Selected data name: " << dataName; + dataNames += dataName + ","; + } + dataNames.pop_back(); + pythonInterface.AddParameter(params.DATA_NAMES, dataNames); + + // Set time range of data to export. + auto timeRange = startTimeStr + "," + stopTimeStr; + pythonInterface.AddParameter(params.TIME_RANGE, timeRange.toStdString()); + + // Set convert resuls for all or only outlet segments. + auto segmentExportType = ui->SegmentExportComboBox->currentText(); + if (segmentExportType == sv4guiSimulationView1d::SegmentExportType::ALL) { + pythonInterface.AddParameter(params.ALL_SEGMENTS, "true"); + } else { + pythonInterface.AddParameter(params.OUTLET_SEGMENTS, "true"); + } // Execute the Python script to generate the 1D solver input file. auto statusMsg = "Converting simulation files ..."; @@ -4452,38 +4492,12 @@ void sv4guiSimulationView1d::ExportResults() if (!status) { QMessageBox::warning(NULL, MsgTitle, "Converting 1D solver results has failed."); - return false; + return; } - -/* - QString msg = ""; - - if(convertedFilesExit) - { - msg="Results have been converted."; - if(!meshFaceDirExits) - msg=msg+"\nNo mesh face dir exits."; - else if(!meshFaceFilesExist) - msg=msg+"\nNo mesh face files exit."; - else if(!calculateFlows) - msg=msg+"\nFail to calculate flows."; - } - else - msg="Results not converted."; - - msg=msg+" "; - - QMessageBox mb(m_Parent); - mb.setWindowTitle("Finished"); - mb.setText(msg); - mb.setIcon(QMessageBox::Information); - mb.setDetailedText(detailedInfo); - mb.setDefaultButton(QMessageBox::Ok); - mb.exec(); - - mitk::StatusBar::GetInstance()->DisplayText("Results converting finished."); -*/ + statusMsg = "Simulation files have been converted."; + ui->JobStatusValueLabel->setText(statusMsg); + mitk::StatusBar::GetInstance()->DisplayText(statusMsg); } //--------------------- @@ -4607,13 +4621,6 @@ void sv4guiSimulationView1d::UpdateSimJob() CheckInputState(); } -/* -void sv4guiSimulationView1d::ShowCalculateFowsWidget(bool checked) -{ - ui->widgetCalculateFlows->setVisible(checked); -} -*/ - #if defined(Q_OS_WIN) QString sv4guiSimulationView1d::FindLatestKey(QString key, QStringList keys) { diff --git a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.h b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.h index 27f8aca75..f2ec8ef4d 100644 --- a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.h +++ b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.h @@ -203,6 +203,16 @@ class sv4guiSimulationView1d : public sv4guiQmitkFunctionality static const std::vector types; }; + class DataExportName { + public: + static const QString AREA; + static const QString FLOW; + static const QString PRESSURE; + static const QString RE; + static const QString WSS; + static const std::vector names; + }; + class CenterlinesSource { public: static const QString CALCULATE; @@ -278,8 +288,6 @@ public slots: void SetupInternalSolverPaths(); - void ShowCalculateFowsWidget(bool checked = false); - public: virtual void CreateQtPartControl(QWidget *parent) override; diff --git a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.ui b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.ui index f1af8b8f1..cdfa9cfe9 100644 --- a/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.ui +++ b/Code/Source/sv4gui/Plugins/org.sv.gui.qt.simulation1d/sv4gui_SimulationView1d.ui @@ -100,8 +100,8 @@ 0 0 - 399 - 567 + 393 + 528 @@ -255,8 +255,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -292,8 +292,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -356,8 +356,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -615,8 +615,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -652,8 +652,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -775,8 +775,8 @@ background-color: white 0 0 - 399 - 567 + 393 + 528 @@ -800,22 +800,7 @@ background-color: white - - - 5 - - - 0 - - - 0 - - - 0 - - - 0 - + @@ -978,7 +963,11 @@ background-color: white - + + + Set the start time for the range of times to convert data. The time is a float simulation time value. + + @@ -988,7 +977,11 @@ background-color: white - + + + Set the stop time for the range of times to convert data. The time is a float simulation time value. + + @@ -1006,26 +999,13 @@ background-color: white - - - - 75 - true - - - - Options: - - - - - Segment + Segments - + @@ -1033,6 +1013,29 @@ background-color: white 0 + + Set the type of segments to convert data. Select <b>Outlet</b> to convert data only for segments that have an outlet boundary condition. <b>All<b> is for all segments. + + + + + + + Data + + + + + + + + 16777215 + 50 + + + + Set the data names to convert. + @@ -1056,19 +1059,22 @@ background-color: white 5 - - - - true - - - Convert ... - - - + + + + true + + + Select the output directory for the converted files and start the conversion process. + + + Convert ... + + + diff --git a/Python/.gitignore b/Python/.gitignore new file mode 100644 index 000000000..d646835b4 --- /dev/null +++ b/Python/.gitignore @@ -0,0 +1,2 @@ +*.pyc +__pycache__/ diff --git a/Python/site-packages/sv_1d_extract_results/extract_results.py b/Python/site-packages/sv_1d_extract_results/extract_results.py index 2ed3636af..36b46d79d 100755 --- a/Python/site-packages/sv_1d_extract_results/extract_results.py +++ b/Python/site-packages/sv_1d_extract_results/extract_results.py @@ -1,9 +1,39 @@ -#!/usr/bin/env python +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -This script is used to extract data from SimVascular 1D solver results files and write them to CSV format files. +This script is used to extract data from SimVascular 1D solver results files and +write them to CSV format files. The script can be executed from the command line +or from a C++ program. -The script can optionally be used to +When run from the command line the script can optionally be used to 1) Plot selected segment data @@ -43,7 +73,7 @@ try: import vtk - from graphics import Graphics + from .graphics import Graphics except ImportError: pass @@ -70,12 +100,15 @@ class Args(object): TIME_RANGE = "time_range" def cmd(name): - """ Create an argparse command argument. + """ + Create an argparse command argument. """ return Args.PREFIX + name.replace("_", "-") def parse_args(): - """ Parse command-line arguments.""" + """ + Parse command-line arguments. + """ parser = argparse.ArgumentParser() parser.add_argument(cmd(Args.ALL_SEGMENTS), action='store_true', @@ -123,7 +156,8 @@ def parse_args(): return parser.parse_args(), parser.print_help def set_parameters(**kwargs): - """ Set the values of parameters input from the command line. + """ + Set the values of parameters input from the command line. """ logger.info("Parse arguments ...") @@ -137,19 +171,21 @@ def set_parameters(**kwargs): if not os.path.exists(params.output_directory): logger.error("The output directory '%s' was not found." % params.output_directory) return None - logger.info("Output directory: '%s'." % params.output_directory) + logger.info("Output directory: '%s'." % params.output_directory) if kwargs.get(Args.RESULTS_DIRECTORY): params.results_directory = kwargs.get(Args.RESULTS_DIRECTORY) if not os.path.exists(params.results_directory): logger.error("The results directory '%s' was not found." % params.results_directory) return None - logger.info("Results directory: '%s'." % params.results_directory) + logger.info("Results directory: '%s'." % params.results_directory) - params.output_file_name = kwargs.get(Args.OUTPUT_FILE) + if kwargs.get(Args.OUTPUT_FILE): + params.output_file_name = kwargs.get(Args.OUTPUT_FILE) logger.info("Output file name: %s" % params.output_file_name) - params.output_format = kwargs.get(Args.OUTPUT_FORMAT) + if kwargs.get(Args.OUTPUT_FORMAT): + params.output_format = kwargs.get(Args.OUTPUT_FORMAT) logger.info("Output format: %s" % params.output_format) params.solver_file_name = kwargs.get(Args.SOLVER_FILE) @@ -157,30 +193,30 @@ def set_parameters(**kwargs): if kwargs.get(Args.DATA_NAMES): params.data_names = kwargs.get(Args.DATA_NAMES).split(",") - logger.info("Data names: %s" % ','.join(params.data_names)) + logger.info("Data names: %s" % ','.join(params.data_names)) if kwargs.get(Args.OUTLET_SEGMENTS): params.outlet_segments = True - logger.info("Outlet segments: %s" % params.outlet_segments) + logger.info("Outlet segments: %s" % params.outlet_segments) if kwargs.get(Args.ALL_SEGMENTS): params.all_segments = True - logger.info("All segments: %s" % params.all_segments) + logger.info("All segments: %s" % params.all_segments) if kwargs.get(Args.SELECT_SEGMENTS): params.select_segment_names = True - logger.info("Select segments: %s" % params.select_segment_names) + logger.info("Select segments: %s" % params.select_segment_names) if kwargs.get(Args.DISPLAY_GEOMETRY): params.display_geometry = (kwargs.get(Args.DISPLAY_GEOMETRY) in ["on", "true"]) - logger.info("Display geometry: %s" % params.display_geometry) + logger.info("Display geometry: %s" % params.display_geometry) if kwargs.get(Args.NODE_SPHERE_RADIUS): params.node_sphere_radius = float(kwargs.get(Args.NODE_SPHERE_RADIUS)) if kwargs.get(Args.PLOT): params.plot_results = (kwargs.get(Args.PLOT) in ["on", "true"]) - logger.info("Plot results: %s" % params.plot_results) + logger.info("Plot results: %s" % params.plot_results) if kwargs.get(Args.SEGMENTS): params.segment_names = kwargs.get(Args.SEGMENTS).split(",") @@ -190,10 +226,14 @@ def set_parameters(**kwargs): params.time_range = [float(s) for s in kwargs.get(Args.TIME_RANGE).split(",")] logger.info("Time range: %s" % ','.join(map(str,params.time_range))) + if not (params.outlet_segments and params.all_segments) and (params.segment_names == None): + logger.warning("No segment options are set therefore no segment data will be read.") + return params def run(**kwargs): - """ Execute the 1D mesh generation using passed parameters. + """ + Execute the convert 1D simulation results using passed parameters. """ result = "" @@ -218,7 +258,8 @@ def run(**kwargs): return result def run_from_c(*args, **kwargs): - """ Execute the convert 1D solver results using passed parameters from c++ + """ + Execute the convert 1D solver results using passed parameters from c++. The '*args' argument contains the directory to write the log file. """ @@ -246,6 +287,9 @@ def run_from_c(*args, **kwargs): return msg if __name__ == '__main__': + """ + Execute the convert 1D solver results form the command line. + """ init_logging() args, print_help = parse_args() params = set_parameters(**vars(args)) @@ -255,13 +299,12 @@ def run_from_c(*args, **kwargs): ## Create graphics interface. # - # Creating a Graphics() object fails is vtk - # is not installed. + # Creating a Graphics() object fails if vtk is not installed. try: graphics = Graphics(params) except: graphics = None - pass + logger.warning("Graphics could not be initialized.") ## Read in the solver file. solver = Solver(params) @@ -288,4 +331,3 @@ def run_from_c(*args, **kwargs): graphics.add_graphics_edges(solver.lines_polydata, solver.lines_segment_names, [0.8, 0.8, 0.8]) graphics.show() - diff --git a/Python/site-packages/sv_1d_extract_results/graphics.py b/Python/site-packages/sv_1d_extract_results/graphics.py index 28ffa01ac..8b0778b68 100644 --- a/Python/site-packages/sv_1d_extract_results/graphics.py +++ b/Python/site-packages/sv_1d_extract_results/graphics.py @@ -1,4 +1,43 @@ -#!/usr/bin/env python +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module is used to display the 1D vessel network. + +The network is displayed in 3D representing nodes as spheres and +segments as cylinders. + +Segments can be selected using the left mouse button. The segment is +highlighted in red and its name is printed. + +""" from os import path import logging @@ -16,19 +55,31 @@ class GeometryColors(object): NODE = [0.0, 0.0, 1.0] class MouseSegmentInteractorStyle(vtk.vtkInteractorStyleTrackballCamera): + """ + The MouseSegmentInteractorStyle class defines a custom graphics + interactor used to select segments. + + Attributes: + picked_actor (vtkActor): The current selected actor. + last_picked_actor (vtkActor): The last selected actor. + graphics (Graphics): The Graphics object. + """ def __init__(self,parent=None): - self.AddObserver("LeftButtonPressEvent", self.leftButtonPressEvent) - self.AddObserver("KeyPressEvent", self.onKeyPressEvent) - self.AddObserver("CharEvent", self.onCharEvent) self.picked_actor = None self.last_picked_actor = None - self.last_picked_property = vtk.vtkProperty() self.graphics = None + + # Add event handling functions. + self.AddObserver("LeftButtonPressEvent", self.leftButtonPressEvent) + self.AddObserver("KeyPressEvent", self.onKeyPressEvent) + self.AddObserver("CharEvent", self.onCharEvent) def leftButtonPressEvent(self,obj,event): """ - Process left mouse button press. + Process a left mouse button press. + + A left mouse button press is used to select a segment actor. """ clickPos = self.GetInteractor().GetEventPosition() picker = vtk.vtkCellPicker() @@ -78,7 +129,8 @@ def onKeyPressEvent(self, renderer, event): ## Save the selected segment. # - if key == 's' and self.graphics.picked_segment and (self.graphics.picked_segment not in self.graphics.picked_segments): + if key == 's' and self.graphics.picked_segment and \ + (self.graphics.picked_segment not in self.graphics.picked_segments): self.graphics.logger.info("Save segment %s" % self.graphics.picked_segment) self.graphics.picked_segments.append(self.graphics.picked_segment) self.graphics.picked_actors.append(self.picked_actor) @@ -104,6 +156,13 @@ def onKeyPressEvent(self, renderer, event): class Graphics(object): """ The Graphics class is used to display the solver mesh in the graphics window. + + Attributes: + segment_actors (dict{str,vtkActor}): A map between segment names and their + actor geometry.} + picked_segment (str): The name of the currently selected segment. + picked_segments (list[str]): The names of saved selected segments. + picked_actors (list[vtkActor]): The acrtors of saved selected segments. """ def __init__(self, params): self.params = params @@ -122,7 +181,8 @@ def __init__(self, params): self.initialize_graphics() def create_graphics_geometry(self, poly_data): - """ Create geometry for display. + """ + Create geometry for display. """ mapper = vtk.vtkPolyDataMapper() mapper.SetInputData(poly_data) @@ -131,7 +191,8 @@ def create_graphics_geometry(self, poly_data): return actor def initialize_graphics(self): - """ Create renderer and graphics window. + """ + Create renderer and graphics window. """ self.renderer = vtk.vtkRenderer() self.window = vtk.vtkRenderWindow() @@ -150,6 +211,9 @@ def initialize_graphics(self): style.SetCurrentRenderer(self.renderer) def add_sphere(self, center, color): + """ + Add a sphere geometry to th graphics scene. + """ sphere = vtk.vtkSphereSource() sphere.SetCenter(center[0], center[1], center[2]) sphere.SetRadius(0.1) @@ -157,6 +221,14 @@ def add_sphere(self, center, color): self.add_graphics_geometry(poly_data, color, True) def add_cyl(self, pt1, pt2, name): + """ + Add a cylinder geometry defined by two points to the graphics scene. + + Arguments: + pt1 (list[float]): The 3D coordinates defining the first end point. + pt2 (list[float]): The 3D coordinates defining the second end point. + name (str): The cylinder (segment) name. + """ cyl = vtk.vtkCylinderSource() cyl.SetRadius(0.05) cyl.SetResolution(15) @@ -166,7 +238,6 @@ def add_cyl(self, pt1, pt2, name): vtk.vtkMath.Subtract(pt2, pt1, x) length = vtk.vtkMath.Norm(x) vtk.vtkMath.Normalize(x) - #print("length: " + str(length)) arbitrary = [0,0,0] arbitrary[0] = vtk.vtkMath.Random(-10,10) @@ -184,10 +255,6 @@ def add_cyl(self, pt1, pt2, name): matrix.SetElement(i, 1, y[i]) matrix.SetElement(i, 2, z[i]) - #print("x: " + str(x)) - #print("y: " + str(y)) - #print("z: " + str(z)) - transform = vtk.vtkTransform() transform.Translate(pt1) transform.Concatenate(matrix); @@ -201,17 +268,17 @@ def add_cyl(self, pt1, pt2, name): mapper = vtk.vtkPolyDataMapper() actor = vtk.vtkActor() - - #mapper.SetInputConnection(transformPD.GetOutputPort()) mapper.SetInputConnection(cyl.GetOutputPort()) actor.SetUserMatrix(transform.GetMatrix()) - actor.SetMapper(mapper) actor.GetProperty().SetColor(*GeometryColors.SEGMENT) self.renderer.AddActor(actor) self.segment_actors[name] = actor def add_graphics_geometry(self, poly_data, color): + """ + Add a vtkPolydata object to the graphics scene. + """ gr_geom = self.create_graphics_geometry(poly_data) gr_geom.GetProperty().SetColor(color[0], color[1], color[2]) gr_geom.GetProperty().SetPointSize(20) @@ -219,6 +286,12 @@ def add_graphics_geometry(self, poly_data, color): self.window.Render() def add_graphics_points(self, poly_data, color): + """ + Add a set of points, represented as spheres, to the graphics scene. + + Arguments: + poly_data (vtkPolydata): The set of points. + """ ball = vtk.vtkSphereSource() ball.SetRadius(self.params.node_sphere_radius) ball.SetThetaResolution(12) @@ -243,6 +316,13 @@ def add_graphics_points(self, poly_data, color): self.window.Render() def add_graphics_edges(self, poly_data, names, color): + """ + Add a set of edges, represented as cylinders, to the graphics scene. + + Arguments: + poly_data (vtkPolydata): The set of edges. + names (list[str]): The names of the cylinders (segments). + """ pt1 = [0,0,0] pt2 = [0,0,0] points = poly_data.GetPoints(); diff --git a/Python/site-packages/sv_1d_extract_results/manage.py b/Python/site-packages/sv_1d_extract_results/manage.py index d6541d3ec..a284d8580 100644 --- a/Python/site-packages/sv_1d_extract_results/manage.py +++ b/Python/site-packages/sv_1d_extract_results/manage.py @@ -1,4 +1,36 @@ -#!/usr/bin/env pytho +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module is used to setup logging. +""" import logging import os diff --git a/Python/site-packages/sv_1d_extract_results/node.py b/Python/site-packages/sv_1d_extract_results/node.py index 7455f4f41..ed8a3c26c 100644 --- a/Python/site-packages/sv_1d_extract_results/node.py +++ b/Python/site-packages/sv_1d_extract_results/node.py @@ -1,4 +1,36 @@ -#!/usr/bin/env python +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module defines a Node class used to store 1D solver node data. +""" from os import path import logging diff --git a/Python/site-packages/sv_1d_extract_results/parameters.py b/Python/site-packages/sv_1d_extract_results/parameters.py index 8563b05f4..c17e02be4 100644 --- a/Python/site-packages/sv_1d_extract_results/parameters.py +++ b/Python/site-packages/sv_1d_extract_results/parameters.py @@ -1,6 +1,40 @@ +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module is used to store the values of parameters set on the command line. +""" class Parameters(): - """ The Parameter class stores the input parameters. + """ + The Parameter class stores the values of input parameters. """ FILE_NAME_SEP = "_" DATA_FILE_EXTENSION = ".dat" @@ -17,7 +51,6 @@ def __init__(self): self.model_name = None self.num_steps = None self.time_step = None - self.times = None self.time_range = None ## Segment names and booleans for selecting segements. diff --git a/Python/site-packages/sv_1d_extract_results/segment.py b/Python/site-packages/sv_1d_extract_results/segment.py index 830986e49..5fc7b77ea 100644 --- a/Python/site-packages/sv_1d_extract_results/segment.py +++ b/Python/site-packages/sv_1d_extract_results/segment.py @@ -1,14 +1,59 @@ +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module defines a Segment class used to store 1D solver segment data. +""" + from os import path import logging from .manage import get_logger_name class Segment(object): + """ + The Segment class is used to store data for a 1D solver segment. + + Attributes: + id (str): The segment ID. + name (str): = name + node1 (Node): The first end node. + node2 (Node): The second end node. + bc_type (str): The segment boundary condition type. See Solver.BcTypes. + data (dict{str,list[]): A map of data names to data (list[list[floats]] + read in for the segment. + """ def __init__(self, id, name, node1, node2, bc_type): self.id = id self.name = name self.node1 = node1 self.node2 = node2 self.bc_type = bc_type - self.data_name = None self.data = None diff --git a/Python/site-packages/sv_1d_extract_results/solver.py b/Python/site-packages/sv_1d_extract_results/solver.py index 5f2fa5a17..c63bd047c 100644 --- a/Python/site-packages/sv_1d_extract_results/solver.py +++ b/Python/site-packages/sv_1d_extract_results/solver.py @@ -1,3 +1,59 @@ +# Copyright (c) Stanford University, The Regents of the University of +# California, and others. +# +# All Rights Reserved. +# +# See Copyright-SimVascular.txt for additional details. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject +# to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +This module is used to read a 1D solver input (.in) and simulation results (.dat) files. + +Functions are defined to write data to CSV format files and create time plots. + +---------------------- + 1D Solver Input File +---------------------- +A solver input file uses keyword statements to defined a simulation. For example + + MODEL SU201_2005_RPA1 + + NODE 0 26.904272079467773 21.049297332763672 22.75612449645996 + + JOINT J0 1 IN0 OUT0 + JOINTINLET IN0 1 0 + JOINTOUTLET OUT0 2 1 6 + + SEGMENT Group0_Seg0 0 25.861634854053026 259 0 1 15.693287129713616 7.8512026064629215 0.0 MAT1 NONE 0.0 0 0 NOBOUND NONE + +The NODE statements define the network geometry and the SEGMENT statements its connectivity. + + + +""" + from os import path import logging from .manage import get_logger_name @@ -25,8 +81,17 @@ class Solver(object): Attributes: read_segment_names (list[string]): List of segment names to read data for. + write_segment_names (list[string]): List of segment names to convert. This is the + same as read_segment_names[] except when interactively selecting segment names + to convert. """ + class StatementKeywords(object): + MODEL = 'MODEL' + NODE = 'NODE' + SEGMENT = 'SEGMENT' + SOLVEROPTIONS = 'SOLVEROPTIONS' + class SegmentFields(object): """ This class defines the location of segment fields in the solver file SEGMENT statement. @@ -49,25 +114,29 @@ class SegmentFields(object): DATA_TABLE_NAME = 16 class BcTypes(object): - """ This class defines boundary condition types. + """ + This class defines boundary condition types. """ - NONE = "NOBOUND" - PRESSURE = "PRESSURE" AREA = "AREA" + CORONARY = "CORONARY" FLOW = "FLOW" + IMPEDANCE = "IMPEDANCE" + NONE = "NOBOUND" + PRESSURE = "PRESSURE" + PRESSURE_WAVE = "PRESSURE_WAVE" + PULMONARY ="PULMONARY" + RCR = "RCR" RESISTANCE = "RESISTANCE" RESISTANCE_TIME = "RESISTANCE_TIME" - PRESSURE_WAVE = "PRESSURE_WAVE" WAVE = "WAVE" - RCR = "RCR" - CORONARY = "CORONARY" - IMPEDANCE = "IMPEDANCE" - PULMONARY ="PULMONARY" def __init__(self, params): self.params = params self.nodes = None self.segments = None + self.times = None + self.data_index_min = None + self.data_index_max = None self.read_segment_names = None self.write_segment_names = None self.graphics = None @@ -87,7 +156,7 @@ def __init__(self, params): def read_solver_file(self): """ - Read in a solver.in file. + Read in a 1D solver .in file. """ self.logger.info("---------- Read solver file ----------") #self.logger.info("Number of points: %d" % num_points) @@ -99,18 +168,17 @@ def read_solver_file(self): line = fp.readline() cnt = 1 while line: - #print("Line {}: {}".format(cnt, line.strip())) line = fp.readline() tokens = line.split() if len(tokens) == 0: continue - if tokens[0] == 'NODE': + if tokens[0] == self.StatementKeywords.NODE: self.add_node(tokens) - elif tokens[0] == 'SEGMENT': + elif tokens[0] == self.StatementKeywords.SEGMENT: self.add_segment(tokens) - elif tokens[0] == 'SOLVEROPTIONS': + elif tokens[0] == self.StatementKeywords.SOLVEROPTIONS: self.add_solver_options(tokens) - elif tokens[0] == 'MODEL': + elif tokens[0] == self.StatementKeywords.MODEL: self.params.model_name = tokens[1] #__while line #__with open(self.params.solver_file_name) as fp: @@ -124,8 +192,11 @@ def read_solver_file(self): if self.params.time_range == None: self.params.time_range = [0.0, self.params.times[-1]] - # Create a points polydata object + # If displaying geometry then create the vtkPolyData + # objects used to display nodes and segments. + # if self.params.display_geometry: + # Create a points polydata object self.points_polydata = vtk.vtkPolyData() self.points_polydata.SetPoints(self.points) self.points_polydata.SetVerts(self.vertices) @@ -152,11 +223,32 @@ def add_solver_options(self, tokens): save_freq = int(tokens[2]) num_steps = int(tokens[3]) self.params.num_steps = num_steps - self.params.times = [i*time_step for i in range(0,num_steps+1,save_freq)] + + ## Set the time data for the given time range. + # + # This creates an array of time values and determines the subset + # of segment data to read in for the given time range + # (data_index_min, self.data_index_max). + # + min_time = self.params.time_range[0] + max_time = self.params.time_range[1] + self.data_index_min = None + self.data_index_max = None + self.times = [] + for i in range(0,num_steps+1,save_freq): + time = i*time_step + if time >= min_time and time <= max_time: + self.times.append(time) + if self.data_index_min == None: + self.data_index_min = i + self.data_index_max = i + #__for i in range(0,num_steps+1,save_freq): + self.logger.info("Number of time values: %d" % len(self.times)) + self.logger.info("Time values: %s" % ','.join(map(str,self.times))) def add_node(self, tokens): """ - Add a node.. + Add a simulation node. """ id = tokens[1] x = float(tokens[2]) @@ -171,10 +263,9 @@ def add_node(self, tokens): def add_segment(self, tokens): """ - Add a segment. + Add a simulation segment. """ fields = self.SegmentFields - name = tokens[fields.NAME] id = tokens[fields.ID] node1 = int(tokens[fields.INLET_NODE]) @@ -185,7 +276,16 @@ def add_segment(self, tokens): def read_segment_data(self): """ - Read in segment data files. + Read in segment data. + + A subset of segment data files can be read in by + + 1) Giving segment names, stored in self.params.segment_names. + + 2) Setting the use outlet segments flag, if self.params.outlet_segments = true. + + All segment data files are read if self.params.all_segments = true. + """ self.read_segment_names = [] @@ -196,14 +296,12 @@ def read_segment_data(self): if self.params.all_segments: self.params.segment_names = [] for name,segment in self.segments.items(): - #self.params.segment_names.append(name) self.read_segment_names.append(name) elif self.params.outlet_segments: self.params.segment_names = [] for name,segment in self.segments.items(): if segment.bc_type != self.BcTypes.NONE: - #self.params.segment_names.append(name) self.read_segment_names.append(name) elif self.params.segment_names: @@ -215,13 +313,21 @@ def read_segment_data(self): return self.logger.info("Read data for segment names: %s" % ','.join(self.read_segment_names)) + self.write_segment_names = [] data_names = self.params.data_names for segment_name in self.read_segment_names: self.read_segment_data_file(segment_name, data_names) + self.write_segment_names.append(segment_name) def read_segment_data_file(self, segment_name, data_names): """ - Read in a segment data file. + Read in a segment data from a .dat file. + + The results are read in for the names of data given in data_names[]. + + Arguments: + segment_name (str): The name of the segment to read data for. + data_names (list[str]): The list of data names for read. """ #self.logger.info("---------- Read segment data file ----------") #self.logger.info("Segment name: %s" % segment_name) @@ -231,14 +337,19 @@ def read_segment_data_file(self, segment_name, data_names): self.logger.error(msg) raise Exception(msg) - segment = self.segments[segment_name] - segment.data = {} sep = Parameters.FILE_NAME_SEP ext = Parameters.DATA_FILE_EXTENSION + results_dir = self.params.results_directory + model_name = self.params.model_name + + imin = self.data_index_min + imax = self.data_index_max + segment = self.segments[segment_name] + segment.data = {} for data_name in data_names: #self.logger.info("Data name: %s" % data_name) - file_name = self.params.results_directory + "/" + self.params.model_name + segment_name + sep + data_name + ext + file_name = results_dir + "/" + model_name + segment_name + sep + data_name + ext num_rows = 0 data = [] @@ -246,7 +357,6 @@ def read_segment_data_file(self, segment_name, data_names): line = fp.readline() cnt = 1 while line: - #print("Line {}: {}".format(cnt, line.strip())) line = fp.readline() tokens = line.split() num_rows += 1 @@ -254,101 +364,113 @@ def read_segment_data_file(self, segment_name, data_names): continue values = [float(v) for v in tokens] num_cols = len(values) - data.append(values) #__while line + + data.append(values[imin:imax+1]) + self.logger.info("read %d data values" % len(values[imin:imax+1])) #__with open(file_name) as fp + segment.data[data_name] = data #__for data_name in self.params.data_names: - #self.logger.info("Number of rows read: %d" % num_rows) - #self.logger.info("Number of columns read: %d" % num_cols) - def write_selected_segments(self, segment_names): """ Write segment data to a file for segments selected interactively. """ - self.write_segment_data(segment_names) + self.write_segment_names = segment_names + self.write_segment_data() - def write_segment_data(self, segment_names=None): + def write_segment_data(self): """ Write segment data to a file. - - This function is called automatically from main() to write any data for - segments that may have been given on the command line. """ - if (segment_names == None) and (self.read_segment_names == None): + if self.write_segment_names == None: return - if segment_names == None: - segment_names = self.read_segment_names + segments = self.segments + segment_names = self.write_segment_names + data_names = self.params.data_names + times = self.times self.logger.info("---------- Write segment data ----------") self.logger.info("Data names: %s" % ','.join(self.params.data_names)) self.logger.info("Segment names: %s" % ','.join(segment_names)) + self.logger.info("File format: %s" % self.params.output_format) + self.logger.info("self.params.output_directory: %s" % self.params.output_directory) + self.logger.info("self.params.output_file_name: %s" % self.params.output_file_name) file_format = self.params.output_format output_dir = self.params.output_directory output_file_name = self.params.output_file_name ext = "." + file_format sep = "_" - self.logger.info("File format: %s" % file_format) - for data_name in self.params.data_names: + ## Check that segements have data. + # + no_data = False + for i,name in enumerate(segment_names): + segment = segments[name] + if segment.data == None: + self.logger.error("Segment '%s' does not have data." % name) + no_data = True + #__for i,name in enumerate(segment_names) + + for data_name in data_names: self.logger.info("Data name: %s" % data_name) file_name = output_dir + "/" + output_file_name + sep + data_name + ext - times = self.params.times with open(file_name, "w") as fp: for i,name in enumerate(segment_names): - self.logger.info("Segment name: %s" % name) + #self.logger.info("Segment name: %s" % name) fp.write(name) if i != len(segment_names)-1: fp.write(",") fp.write("\n") for i,time in enumerate(times): - self.logger.info("time: %g" % time) + #self.logger.info("time: %g" % time) fp.write(str(time) + ",") for j,name in enumerate(segment_names): - segment = self.segments[name] + segment = segments[name] + if segment.data == None: + continue data_list = segment.data[data_name] data = data_list[-1] fp.write(str(data[i])) if j != len(segment_names)-1: fp.write(",") - #__for j,name in enumerate(self.params.segments) + #__for j,name in enumerate(segment_names) fp.write("\n") #__for i,time in enumerate(times): - - #__for data_name in self.params.data_names + #__for data_name in data_names def plot_results(self): - """ Plot results. + """ + Plot results. """ self.logger.info("---------- Plot results ----------") title = self.params.solver_file_name min_time = self.params.time_range[0] max_time = self.params.time_range[1] + times = self.times self.logger.info("Min time: %f" % min_time) self.logger.info("Max time: %f" % max_time) for data_name in self.params.data_names: self.logger.info("Data name: %s" % data_name) - times = self.params.times plot_values = [] plot_names = [] fig, ax = plt.subplots() ylabel = data_name for j,name in enumerate(self.read_segment_names): + segment = self.segments[name] + data_list = segment.data[data_name] + data = data_list[-1] values = [] plot_times = [] for i,time in enumerate(times): - if (time > min_time) and (time <= max_time): - segment = self.segments[name] - data_list = segment.data[data_name] - data = data_list[-1] - values.append(data[i]) - plot_times.append(time) + values.append(data[i]) + plot_times.append(time) #__for i,time in enumerate(times) plot_values.append(values) plot_names.append(name) @@ -439,7 +561,8 @@ def plot_segment(self, segment_name, data_name): plt.show() def press_key(self, event): - """ Add key events. + """ + Key press event handler for plots. Keys: q: key to quit. @@ -447,4 +570,3 @@ def press_key(self, event): if event.key == 'q': plt.close(event.canvas.figure) -