From 3a72641935f126ef177f160d279fff5a661b64a7 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Sun, 29 Dec 2024 10:16:46 -0500 Subject: [PATCH 1/4] Add JSON parser for input options This change introduces a JSON parser and serializer for handling input options. While the overall implementation is incomplete, key functionality has been added. Changes: - Add nlohmann json to CMakeLists.txt - Update to C++20 - Update CMake version as needed - Add JsonParser for parsing a single options file - Add JsonSerializer for serializing a single options file - Refactor existing serialization to "LegacySerializer" - Remove: cvDoubleVec jointXcoord cvDoubleVec jointYcoord cvDoubleVec jointZcoord from options (unused) - Add "nodeAssociation" to joints list for explicit node associations in future updates TODO: - Add support for nested option files in JSON parser - Add a converter for legacy format - Update joint creation to use explicit node associations - Update legacy parser to provide implicit node associations so they can be reused - Verify changes against existing tests - Add tests for parser, converter, etc. --- .gitignore | 3 + CMakeLists.txt | 14 +- Code/Source/cvOneDOptions.cxx | 439 ++++--------- Code/Source/cvOneDOptions.h | 59 +- Code/Source/cvOneDOptionsJsonParser.cxx | 245 +++++++ Code/Source/cvOneDOptionsJsonParser.h | 49 ++ Code/Source/cvOneDOptionsJsonSerializer.cxx | 272 ++++++++ Code/Source/cvOneDOptionsJsonSerializer.h | 49 ++ Code/Source/cvOneDOptionsLegacySerializer.cxx | 598 ++++++++++++++++++ Code/Source/cvOneDOptionsLegacySerializer.h | 50 ++ Code/Source/main.cxx | 378 +---------- Code/Source/main.h | 6 +- Code/Source/testJson.cxx | 89 +++ Code/Source/testJson.h | 6 + 14 files changed, 1541 insertions(+), 716 deletions(-) create mode 100644 Code/Source/cvOneDOptionsJsonParser.cxx create mode 100644 Code/Source/cvOneDOptionsJsonParser.h create mode 100644 Code/Source/cvOneDOptionsJsonSerializer.cxx create mode 100644 Code/Source/cvOneDOptionsJsonSerializer.h create mode 100644 Code/Source/cvOneDOptionsLegacySerializer.cxx create mode 100644 Code/Source/cvOneDOptionsLegacySerializer.h create mode 100644 Code/Source/testJson.cxx create mode 100644 Code/Source/testJson.h diff --git a/.gitignore b/.gitignore index 45d7081..c28e83e 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ build .cproject .project .settings/ + +# Vscode +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index c3dc347..7f5e487 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) +CMAKE_MINIMUM_REQUIRED(VERSION 3.11) # COMMON SETTINGS PROJECT(OneDSolver) @@ -74,7 +74,7 @@ ENDIF() # FILE(COPY "${CMAKE_CURRENT_SOURCE_DIR}/tests" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") # COMPILER FLAGS -SET(CMAKE_CXX_FLAGS "-g -m64 -O3 -std=c++0x -fPIC") +SET(CMAKE_CXX_FLAGS "-g -m64 -O3 -std=c++20 -fPIC") # PLACE EXECUTABLE IN BIN FOLDER SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) @@ -109,3 +109,13 @@ ENDIF() if (BUILD_SV_INSTALLER) add_subdirectory("${CMAKE_SOURCE_DIR}/Distribution") ENDIF() + +# NLOHMANN JSON FOR SERIALIZATION +include(FetchContent) +FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.11.2 # You can specify a version or use the latest tag +) +FetchContent_MakeAvailable(nlohmann_json) +target_include_directories(${PROJECT_NAME} PUBLIC ${nlohmann_json_SOURCE_DIR}/single_include) diff --git a/Code/Source/cvOneDOptions.cxx b/Code/Source/cvOneDOptions.cxx index d654e20..7fadbb2 100644 --- a/Code/Source/cvOneDOptions.cxx +++ b/Code/Source/cvOneDOptions.cxx @@ -31,362 +31,161 @@ #include "cvOneDOptions.h" -// CONSTRUCTOR -cvOneDOptions::cvOneDOptions(){ - modelNameDefined = false; - solverOptionDefined = false; -} +namespace cvOneD{ -// DESTRUCTOR -cvOneDOptions::~cvOneDOptions(){ -} +// ===================== +// PERFORM DATA CHECKING +// ===================== -// PRINT MODEL NAME -void cvOneDOptions::printModelName(FILE* f){ - fprintf(f,"--- \n"); - fprintf(f,"MODEL NAME: %s\n",modelName.c_str()); -} +namespace{ -// PRINT NODE DATA -void cvOneDOptions::printNodeData(FILE* f){ - fprintf(f,"--- \n"); - fprintf(f,"NODE DATA\n"); - for(long int loopA=0;loopA + int checkForDuplicateEntry(vector vec){ + for(int loopA=0;loopA + void checkForDuplicateValues(const vector& vec, const string& type) { + if (int dblIDX = checkForDuplicateEntry(vec); dblIDX >= 0) { + throw cvException(string("ERROR: Duplicate " + type + ": " + vec[dblIDX] + "\n").c_str()); + } } -} -// PRINT SEGMEMENT DATA -void cvOneDOptions::printSegmentData(FILE* f){ - fprintf(f,"--- \n"); - fprintf(f,"SEGMENT DATA\n"); - for(long int loopA=0;loopA= 0) { + throw cvException(string("ERROR: Duplicate " + type + ": " + to_string(vec[dblIDX]) + "\n").c_str()); + } } -} -// PRINT SOLVER OPTION DATA -void cvOneDOptions::printSolverOptions(FILE* f){ - fprintf(f,"--- \n"); - fprintf(f,"PRINT SOLVER OPTION DATA\n"); - fprintf(f,"TIME STEP: %f\n",timeStep); - fprintf(f,"STEP SIZE: %ld\n",stepSize); - fprintf(f,"MAX STEP: %ld\n",maxStep); - fprintf(f,"QUADRATURE POINTS: %ld\n",quadPoints); - fprintf(f,"INLET DATA TABLE: %s\n",inletDataTableName.c_str()); - fprintf(f,"BOUNDARY TYPE: %s\n",boundaryType.c_str()); - fprintf(f,"CONVERGENCE TOLERANCE: %f\n",convergenceTolerance); - fprintf(f,"USE IV: %d\n",useIV); - fprintf(f,"USE STABILIZATION: %d\n",useStab); -} - -// PRINT MATERIAL DATA -void cvOneDOptions::printMaterialData(FILE* f){ - fprintf(f,"--- \n"); - // MATERIAL - for(long int loopA=0;loopA 0){ - fprintf(f,"LIST ITEMS\n"); - for(size_t loopB=0;loopB 0){ - fprintf(f,"LIST ITEMS\n"); - for(size_t loopB=0;loopB + void checkForNegativeValues(const vector& vec, const vector& names, const string& type) { + if (int chkIDX = checkForPositiveVal(vec); chkIDX >= 0) { + throw cvException(string("ERROR: Negative " + type + " in segment: " + names[chkIDX] + "\n").c_str()); } - } - fprintf(f,"\n"); } -} -// PRINT DATA TABLES -void cvOneDOptions::printDataTables(FILE* f){ - fprintf(f,"--- \n"); - fprintf(f,"DATA TABLES\n"); - for(long int loopA=0;loopA -int checkForDoubleEntry(vector vec){ - for(int loopA=0;loopA + bool checkContains(T value, vector vec){ + for(int loopA=0;loopA -bool checkContains(T value, vector vec){ - for(int loopA=0;loopA= opts.nodeName.size())){ + throw cvException(string("ERROR: Missing Node in Segment: " + opts.segmentName[loopA] + "\n").c_str()); + } + if((outNode < 0)||(outNode >= opts.nodeName.size())){ + throw cvException(string("ERROR: Missing Node in Segment: " + opts.segmentName[loopA] + "\n").c_str()); + } } } - return false; -} -// CHECK FOR POSITIVE VALUES -int checkForPositiveVal(cvDoubleVec vec){ - for(int loopA=0;loopA= 0){ - throw cvException(string("ERROR: Double Node Name: " + nodeName[dblIDX] + "\n").c_str()); - } - // Check for double joint name - dblIDX = checkForDoubleEntry(jointName); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double Joint Name: " + jointName[dblIDX] + "\n").c_str()); - } - // Check for double jointInlet name - dblIDX = checkForDoubleEntry(jointInletListNames); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double JointInlet Name: " + jointInletListNames[dblIDX] + "\n").c_str()); - } - // Check for double jointOutlet name - dblIDX = checkForDoubleEntry(jointOutletListNames); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double JointOutlet Name: " + jointOutletListNames[dblIDX] + "\n").c_str()); - } - // Check for double material name - dblIDX = checkForDoubleEntry(materialName); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double Material Name: " + materialName[dblIDX] + "\n").c_str()); - } - // Check for double data table name - dblIDX = checkForDoubleEntry(dataTableName); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double Data Table Name: " + dataTableName[dblIDX] + "\n").c_str()); - } - // Check for double segment Name - dblIDX = checkForDoubleEntry(segmentName); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double Segment Name: " + segmentName[dblIDX] + "\n").c_str()); - } - // Check for double segment ID - dblIDX = checkForDoubleEntry(segmentID); - if(dblIDX >= 0){ - throw cvException(string("ERROR: Double Segment ID: " + to_string(segmentID[dblIDX]) + "\n").c_str()); - } - // Check for negative area in input - chkIDX = checkForPositiveVal(segmentInInletArea); - if(chkIDX >= 0){ - throw cvException(string("ERROR: Negative Inlet area in segment : " + segmentName[chkIDX] + "\n").c_str()); - } - chkIDX = checkForPositiveVal(segmentInOutletArea); - if(chkIDX >= 0){ - throw cvException(string("ERROR: Negative Outlet area in segment : " + segmentName[chkIDX] + "\n").c_str()); - } + // Check if there are duplicate options + checkForDuplicates(opts); + + // Check for negative values when there shouldn't be + checkForNegatives(opts); // Check the consistency between node coords and segment lengths - checkSegmentLengthConsistency(); + checkSegmentLengthConsistency(opts); // Check if the segments refer to a node that is not there - checkSegmentHasNodes(); + checkSegmentHasNodes(opts); // Check if the joints refer to a node that is not there - checkJointHasNodes(); + checkJointHasNodes(opts); } -void cvOneDOptions::checkSegmentHasNodes(){ - int inNode = 0; - int outNode = 0; - for(int loopA=0;loopA= nodeName.size())){ - throw cvException(string("ERROR: Missing Node in Segment: " + segmentName[loopA] + "\n").c_str()); - } - if((outNode < 0)||(outNode >= nodeName.size())){ - throw cvException(string("ERROR: Missing Node in Segment: " + segmentName[loopA] + "\n").c_str()); - } - } -} - -void cvOneDOptions::checkJointHasNodes(){ - string currNodeName; - for(int loopA=0;loopA -# include -# include "cvOneDTypes.h" -# include "cvOneDException.h" +#include +#include +#include +#include "cvOneDTypes.h" +#include "cvOneDException.h" using namespace std; -class cvOneDOptions{ - public: - // CONSTRUCTOR - cvOneDOptions(); - // DESTRUCTOR - ~cvOneDOptions(); +namespace cvOneD{ + +struct options{ // DATA string modelName; - bool modelNameDefined; // NODE DATA cvStringVec nodeName; + // Note that these coordinates are also used + // for the joints through an implicit mapping cvDoubleVec nodeXcoord; cvDoubleVec nodeYcoord; cvDoubleVec nodeZcoord; // JOINT DATA cvStringVec jointName; - cvStringVec jointNode; - cvDoubleVec jointXcoord; - cvDoubleVec jointYcoord; - cvDoubleVec jointZcoord; + + // "jointNode" isn't used currently -- but we want + // to start integrating it. It can be used to make + // the mapping from joint->node explicit. Right + // now, the mapping is implicit: each joint is + // associated with the node of the same index in + // the list of nodes. + cvStringVec jointNode; + cvStringVec jointInletName; cvStringVec jointOutletName; - // JOINT INLET AND OUTLET LIST cvStringVec jointInletListNames; cvLongVec jointInletListNumber; cvLongMat jointInletList; + cvStringVec jointOutletListNames; cvLongVec jointOutletListNumber; cvLongMat jointOutletList; @@ -118,26 +122,11 @@ class cvOneDOptions{ int useIV; int useStab; string outputType; - bool solverOptionDefined; - - // PRINTING - void printToFile(string fileName); - void printModelName(FILE* f); - void printNodeData(FILE* f); - void printJointData(FILE* f); - void printSegmentData(FILE* f); - void printSolverOptions(FILE* f); - void printMaterialData(FILE* f); - void printJointInletData(FILE* f); - void printJointOutletData(FILE* f); - void printDataTables(FILE* f); - - // CHECKING - void check(); - void checkSegmentLengthConsistency(); - void checkSegmentHasNodes(); - void checkJointHasNodes(); }; +void validateOptions(options const& opts); + +} // namespace cvOneD + #endif // CVONEDOPTIONS_H diff --git a/Code/Source/cvOneDOptionsJsonParser.cxx b/Code/Source/cvOneDOptionsJsonParser.cxx new file mode 100644 index 0000000..6945722 --- /dev/null +++ b/Code/Source/cvOneDOptionsJsonParser.cxx @@ -0,0 +1,245 @@ +#include +#include +#include + +#include "cvOneDOptionsJsonSerializer.h" + +using json = nlohmann::ordered_json; + +namespace cvOneD{ + +namespace { + +std::string readFileContents(const std::string& filePath) { + std::ifstream file(filePath); + if (!file.is_open()) { + throw std::runtime_error("Could not open file: " + filePath); + } + + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +// Helper function for error handling +template +void wrapParsingErrors(const std::string& sectionName, Func&& func) { + try { + func(); + } catch (const std::exception& e) { + throw std::runtime_error("Error parsing '" + sectionName + "': " + std::string(e.what())); + } +} + +void parseNodeData(const nlohmann::ordered_json& jsonData, options& opts) try { + // Ensure "nodes" exists and is an array + if (!jsonData.contains("nodes") || !jsonData["nodes"].is_array()) { + throw std::invalid_argument("Expected 'nodes' to be an array in JSON input."); + } + + const auto& nodes = jsonData["nodes"]; + + for (const auto& node : nodes) { + if (!node.is_object()) { + throw std::invalid_argument("Each entry in 'nodes' must be a JSON object."); + } + + // Ensure required fields exist + opts.nodeName.push_back(node.at("name").get()); + opts.nodeXcoord.push_back(node.at("x").get()); + opts.nodeYcoord.push_back(node.at("y").get()); + opts.nodeZcoord.push_back(node.at("z").get()); + } +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing 'nodes': " + std::string(e.what())); +} + +void parseJointData(const nlohmann::ordered_json& jsonData, options& opts) try { + // Ensure "joints" exists and is an array + if (!jsonData.contains("joints") || !jsonData["joints"].is_array()) { + throw std::invalid_argument("Expected 'joints' to be an array in JSON input."); + } + + const auto& joints = jsonData["joints"]; + + for (const auto& jointEntry : joints) { + if (!jointEntry.is_object()) { + throw std::invalid_argument("Each entry in 'joints' must be a JSON object."); + } + + // Parse JOINT data + const auto& joint = jointEntry.at("joint"); // Ensures "joint" exists + opts.jointName.push_back(joint.at("id").get()); + opts.nodeName.push_back(joint.at("associatedNode").get()); + opts.jointInletListNames.push_back(joint.at("inletName").get()); + opts.jointOutletListNames.push_back(joint.at("outletName").get()); + + // Parse JOINTINLET data + const auto& jointInlet = jointEntry.at("jointInlet"); // Ensures "jointInlet" exists + opts.jointInletListNames.push_back(jointInlet.at("name").get()); + opts.jointInletListNumber.push_back(jointInlet.at("totalSegments").get()); + opts.jointInletList.emplace_back(jointInlet.at("segments").get>()); + + // Parse JOINTOUTLET data + const auto& jointOutlet = jointEntry.at("jointOutlet"); // Ensures "jointOutlet" exists + opts.jointOutletListNames.push_back(jointOutlet.at("name").get()); + opts.jointOutletListNumber.push_back(jointOutlet.at("totalSegments").get()); + opts.jointOutletList.emplace_back(jointOutlet.at("segments").get>()); + } + +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing 'joints': " + std::string(e.what())); +} + +void parse_material_data(const nlohmann::json& json, options& opts) try { + if (!json.contains("materials") || !json["materials"].is_array()) { + throw std::invalid_argument("Expected 'materials' to be an array in JSON input."); + } + + const auto& materials = json["materials"]; + + for (const auto& material : materials) { + if (!material.is_object()) { + throw std::invalid_argument("Each material in 'materials' must be a JSON object."); + } + + opts.materialName.push_back(material.at("name").get()); + opts.materialType.push_back(material.at("type").get()); + opts.materialDensity.push_back(material.at("density").get()); + opts.materialViscosity.push_back(material.at("viscosity").get()); + opts.materialPRef.push_back(material.at("pRef").get()); + opts.materialExponent.push_back(material.at("exponent").get()); + opts.materialParam1.push_back(material.at("param1").get()); + opts.materialParam2.push_back(material.at("param2").get()); + opts.materialParam3.push_back(material.at("param3").get()); + } + +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing material data: " + std::string(e.what())); +} + +void parseDataTables(const nlohmann::ordered_json& jsonData, options& opts) try { + if (!jsonData.contains("dataTables") || !jsonData["dataTables"].is_array()) { + throw std::invalid_argument("Expected 'dataTables' to be an array in JSON input."); + } + + const auto& dataTables = jsonData["dataTables"]; + + // Parse each data table entry + for (const auto& table : dataTables) { + if (!table.is_object()) { + throw std::invalid_argument("Each entry in 'dataTables' must be a JSON object."); + } + + // Ensure required fields exist and have the correct types + const auto& name = table.at("name").get(); + const auto& type = table.at("type").get(); + + // Parse "values" array + if (!table.contains("values") || !table["values"].is_array()) { + throw std::invalid_argument("Expected 'values' to be an array in each data table."); + } + + cvDoubleVec values; + for (const auto& val : table["values"]) { + if (!val.is_number()) { + throw std::invalid_argument("Each 'values' entry must be a number."); + } + values.push_back(val.get()); + } + + // Append parsed data to the options object + opts.dataTableName.push_back(name); + opts.dataTableType.push_back(type); + opts.dataTableVals.push_back(values); + } +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing 'dataTables': " + std::string(e.what())); +} + +void parseSegmentData(const nlohmann::ordered_json& jsonData, options& opts) try { + const auto& segments = jsonData.at("segments"); // Throws if "segments" does not exist + if (!segments.is_array()) { + throw std::invalid_argument("'segments' must be an array."); + } + + for (const auto& segment : segments) { + if (!segment.is_object()) { + throw std::invalid_argument("Each entry in 'segments' must be a JSON object."); + } + + // Parse required fields without defaults; throw if missing + opts.segmentName.push_back(segment.at("name").get()); + opts.segmentID.push_back(segment.at("id").get()); + opts.segmentLength.push_back(segment.at("length").get()); + opts.segmentTotEls.push_back(segment.at("totalElements").get()); + opts.segmentInNode.push_back(segment.at("inNode").get()); + opts.segmentOutNode.push_back(segment.at("outNode").get()); + opts.segmentInInletArea.push_back(segment.at("inletArea").get()); + opts.segmentInOutletArea.push_back(segment.at("outletArea").get()); + opts.segmentInFlow.push_back(segment.at("flow").get()); + opts.segmentMatName.push_back(segment.at("materialName").get()); + opts.segmentLossType.push_back(segment.at("lossType").get()); + opts.segmentBranchAngle.push_back(segment.at("branchAngle").get()); + opts.segmentUpstreamSegment.push_back(segment.at("upstreamSegment").get()); + opts.segmentBranchSegment.push_back(segment.at("branchSegment").get()); + opts.segmentBoundType.push_back(segment.at("boundaryType").get()); + opts.segmentDataTableName.push_back(segment.at("dataTableName").get()); + } +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing segment data: " + std::string(e.what())); +} + +void parseSolverOptions(const nlohmann::ordered_json& jsonData, options& opts) try { + // Ensure "solverOptions" section exists + const auto& solverOptions = jsonData.at("solverOptions"); + + opts.timeStep = solverOptions.at("timeStep").get(); + opts.stepSize = solverOptions.at("stepSize").get(); + opts.maxStep = solverOptions.at("maxStep").get(); + opts.quadPoints = solverOptions.at("quadPoints").get(); + opts.inletDataTableName = solverOptions.at("inletDataTableName").get(); + opts.boundaryType = solverOptions.at("boundaryType").get(); + opts.convergenceTolerance = solverOptions.at("convergenceTolerance").get(); + opts.useIV = solverOptions.at("useIV").get(); + opts.useStab = solverOptions.at("useStab").get(); + + // Doesn't seem like this is used...but we'll provide a default anyway + opts.outputType = solverOptions.value("outputType", "None"); + +} catch (const std::exception& e) { + throw std::runtime_error("Error parsing 'solverOptions': " + std::string(e.what())); +} + +options parseJsonOptions(const std::string& jsonString) { + json jsonData; + options opts; + + try{ + jsonData = json::parse(jsonString); + } catch (const std::exception& e) { + throw std::runtime_error("Error parsing json file: " + std::string(e.what())); + } + + opts.modelName = jsonData.value("modelName", ""); + parseNodeData(jsonData,opts); + parseJointData(jsonData,opts); + parse_material_data(jsonData,opts); + parseDataTables(jsonData,opts); + parseSegmentData(jsonData, opts); + parseSolverOptions(jsonData, opts); + + return opts; +} + +} // namespace + +options readJsonOptions(std::string const& inputFile){ + auto const jsonStr = readFileContents(inputFile); + + return parseJsonOptions(jsonStr); +} + +} // namespace cvOneD + + diff --git a/Code/Source/cvOneDOptionsJsonParser.h b/Code/Source/cvOneDOptionsJsonParser.h new file mode 100644 index 0000000..da57030 --- /dev/null +++ b/Code/Source/cvOneDOptionsJsonParser.h @@ -0,0 +1,49 @@ + +/* 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. + */ + +#ifndef CVONEDOPTIONSJSONPARSER_H +#define CVONEDOPTIONSJSONPARSER_H + +#include "cvOneDOptions.h" + +using namespace std; + +namespace cvOneD{ + +options readJsonOptions(string const& inputFile); + +} // namespace cvOneD + +#endif // CVONEDOPTIONSJSONPARSER_H + + + diff --git a/Code/Source/cvOneDOptionsJsonSerializer.cxx b/Code/Source/cvOneDOptionsJsonSerializer.cxx new file mode 100644 index 0000000..be9bf29 --- /dev/null +++ b/Code/Source/cvOneDOptionsJsonSerializer.cxx @@ -0,0 +1,272 @@ +#include +#include +#include + +#include "cvOneDOptionsJsonSerializer.h" + +using json = nlohmann::ordered_json; + +namespace cvOneD{ + +namespace { + +// Serialize cvStringVec into a JSON array (as a plain array) +nlohmann::ordered_json to_json(const cvStringVec& vec) { + nlohmann::ordered_json j = nlohmann::ordered_json::array(); + for (const auto& str : vec) { + j.push_back(str); + } + return j; +} + +// Serialize cvLongVec into a JSON array (as a plain array) +nlohmann::ordered_json to_json(const cvLongVec& vec) { + nlohmann::ordered_json j = nlohmann::ordered_json::array(); + for (const auto& num : vec) { + j.push_back(num); + } + return j; +} + +// Serialize cvDoubleVec into a JSON array (as a plain array) +nlohmann::ordered_json to_json(const cvDoubleVec& vec) { + nlohmann::ordered_json j = nlohmann::ordered_json::array(); + for (const auto& num : vec) { + j.push_back(num); + } + return j; +} + +// Serialize cvLongMat into a JSON array of arrays (matrix) +nlohmann::ordered_json to_json(const cvLongMat& mat) { + nlohmann::ordered_json j = nlohmann::ordered_json::array(); + for (const auto& row : mat) { + nlohmann::ordered_json jRow = nlohmann::ordered_json::array(); + for (const auto& val : row) { + jRow.push_back(val); + } + j.push_back(jRow); + } + return j; +} + +template +void check_consistent_size(const std::string& dataName, const Containers&... containers) { + auto size = [](const auto& container) { return container.size(); }; + if (!((size(containers) == size(std::get<0>(std::tie(containers...)))) && ...)) { + throw std::runtime_error(dataName + ": All containers must have the same size."); + } +} + +// Serialize Node data into a JSON array, returning only the array +nlohmann::ordered_json serialize_node_data(const cvStringVec& nodeName, const cvDoubleVec& nodeXcoord, const cvDoubleVec& nodeYcoord, const cvDoubleVec& nodeZcoord) { + nlohmann::ordered_json nodes = nlohmann::ordered_json::array(); + + check_consistent_size("nodes", nodeName, nodeXcoord, nodeYcoord, nodeZcoord); + + size_t n = nodeName.size(); + for (size_t i = 0; i < n; ++i) { + nlohmann::ordered_json node; + node["name"] = nodeName.at(i); + node["x"] = nodeXcoord.at(i); + node["y"] = nodeYcoord.at(i); + node["z"] = nodeZcoord.at(i); + nodes.push_back(node); + } + + return nodes; // Just return the array of nodes +} + +nlohmann::ordered_json serialize_joint_data(const options& opts) { + nlohmann::ordered_json j = nlohmann::ordered_json::array(); + + // Check consistency of the joint inlet and outlet vectors + check_consistent_size("joints", opts.jointName, + opts.jointInletListNames, opts.jointInletListNumber, + opts.jointOutletListNames, opts.jointOutletListNumber, + opts.jointInletList, opts.jointOutletList); + + size_t jointCount = opts.jointName.size(); + + // There must be enough nodes for us to make associations. This is + // an implicit assumption made in the current joint creation code. + if( opts.nodeName.size() < jointCount ){ + throw std::runtime_error("The implicit joint-node association requires that there be at least as many nodes as there are joints."); + } + + for (size_t i = 0; i < jointCount; ++i) { + nlohmann::ordered_json joint; + + // Add JOINT data + // We're adding the node based on the index so we + // can start incorporating the explicit association + // from the implicit one currently used in the code. + joint["joint"] = { + {"id", "J" + std::to_string(i + 1)}, + {"associatedNode", opts.nodeName.at(i)}, + {"inletName", opts.jointInletListNames.at(i)}, + {"outletName", opts.jointOutletListNames.at(i)} + }; + + // Add JOINTINLET data + joint["jointInlet"] = { + {"name", opts.jointInletListNames.at(i)}, + {"totalSegments", opts.jointInletListNumber.at(i)}, + {"segments", to_json(opts.jointInletList.at(i))} + }; + + // Add JOINTOUTLET data + joint["jointOutlet"] = { + {"name", opts.jointOutletListNames.at(i)}, + {"totalSegments", opts.jointOutletListNumber.at(i)}, + {"segments", to_json(opts.jointOutletList.at(i))} + }; + + j.push_back(joint); + } + + return j; +} + +nlohmann::ordered_json serialize_material_data(const options& opts) { + nlohmann::ordered_json materials = nlohmann::ordered_json::array(); + + // Ensure all material-related vectors are the same size + check_consistent_size("materials", + opts.materialName, opts.materialType, opts.materialDensity, + opts.materialViscosity, opts.materialPRef, opts.materialExponent, + opts.materialParam1, opts.materialParam2, opts.materialParam3); + + size_t n = opts.materialName.size(); + for (size_t i = 0; i < n; ++i) { + nlohmann::ordered_json material; + material["name"] = opts.materialName.at(i); + material["type"] = opts.materialType.at(i); + material["density"] = opts.materialDensity.at(i); + material["viscosity"] = opts.materialViscosity.at(i); + material["pRef"] = opts.materialPRef.at(i); + material["exponent"] = opts.materialExponent.at(i); + material["param1"] = opts.materialParam1.at(i); + material["param2"] = opts.materialParam2.at(i); + material["param3"] = opts.materialParam3.at(i); + materials.push_back(material); + } + + return materials; // Just return the array of materials +} + +// Serialize data tables into a JSON array +nlohmann::ordered_json serialize_data_tables(const cvStringVec& dataTableName, + const cvStringVec& dataTableType, + const std::vector& dataTableVals) { + nlohmann::ordered_json dataTables = nlohmann::ordered_json::array(); + + check_consistent_size("data tables", dataTableName, dataTableType, dataTableVals); + + size_t n = dataTableName.size(); + for (size_t i = 0; i < n; ++i) { + nlohmann::ordered_json table; + table["name"] = dataTableName.at(i); + table["type"] = dataTableType.at(i); + table["values"] = to_json(dataTableVals.at(i)); // Use the existing `to_json` for cvDoubleVec + dataTables.push_back(table); + } + + return dataTables; // Return the array of data tables +} + +nlohmann::ordered_json serializeSegmentData(const options& opts) { + nlohmann::ordered_json segments = nlohmann::ordered_json::array(); + + // Ensure all segment-related vectors are the same size + check_consistent_size("segments", opts.segmentName, opts.segmentID, opts.segmentLength, + opts.segmentTotEls, opts.segmentInNode, opts.segmentOutNode, + opts.segmentInInletArea, opts.segmentInOutletArea, opts.segmentInFlow, + opts.segmentMatName, opts.segmentLossType, opts.segmentBranchAngle, + opts.segmentUpstreamSegment, opts.segmentBranchSegment, + opts.segmentBoundType, opts.segmentDataTableName); + + size_t n = opts.segmentName.size(); + for (size_t i = 0; i < n; ++i) { + nlohmann::ordered_json segment; + + segment["name"] = opts.segmentName.at(i); + segment["id"] = opts.segmentID.at(i); + segment["length"] = opts.segmentLength.at(i); + segment["totalElements"] = opts.segmentTotEls.at(i); + segment["inNode"] = opts.segmentInNode.at(i); + segment["outNode"] = opts.segmentOutNode.at(i); + segment["inletArea"] = opts.segmentInInletArea.at(i); + segment["outletArea"] = opts.segmentInOutletArea.at(i); + segment["flow"] = opts.segmentInFlow.at(i); + segment["materialName"] = opts.segmentMatName.at(i); + segment["lossType"] = opts.segmentLossType.at(i); + segment["branchAngle"] = opts.segmentBranchAngle.at(i); + segment["upstreamSegment"] = opts.segmentUpstreamSegment.at(i); + segment["branchSegment"] = opts.segmentBranchSegment.at(i); + segment["boundaryType"] = opts.segmentBoundType.at(i); + segment["dataTableName"] = opts.segmentDataTableName.at(i); + + segments.push_back(segment); + } + + return segments; +} + +nlohmann::ordered_json serializeSolverOptions(options const& opts) { + nlohmann::ordered_json solverOptions; + + solverOptions["timeStep"] = opts.timeStep; + solverOptions["stepSize"] = opts.stepSize; + solverOptions["maxStep"] = opts.maxStep; + solverOptions["quadPoints"] = opts.quadPoints; + solverOptions["inletDataTableName"] = opts.inletDataTableName; + solverOptions["boundaryType"] = opts.boundaryType; + solverOptions["convergenceTolerance"] = opts.convergenceTolerance; + solverOptions["useIV"] = opts.useIV; + solverOptions["useStab"] = opts.useStab; + solverOptions["outputType"] = opts.outputType; + + return solverOptions; +} + +// Serialize all options into a single JSON object +nlohmann::ordered_json serialize_options(const options& opts) { + nlohmann::ordered_json j; + + // Serialize and add each part of the options data + j["modelName"] = opts.modelName; + + j["solverOptions"] = serializeSolverOptions(opts); + + j["materials"] = serialize_material_data(opts); + + j["nodes"] = serialize_node_data(opts.nodeName, opts.nodeXcoord, opts.nodeYcoord, opts.nodeZcoord); + + j["joints"] = serialize_joint_data(opts); + + j["segments"] = serializeSegmentData(opts); + + j["dataTables"] = serialize_data_tables(opts.dataTableName, opts.dataTableType, opts.dataTableVals); + + return j; +} + +} // namespace + +void writeJsonOptions(options const& opts, std::string const& fileName){ + // Serialize options into JSON + nlohmann::ordered_json jsonData = cvOneD::serialize_options(opts); + + // Write JSON to the specified file + std::ofstream outFile(fileName); + if (!outFile) { + throw std::runtime_error("Unable to open file for writing: " + fileName); + } + outFile << jsonData.dump(2); // Write with 2-space indentation + outFile.close(); +} + +} // namespace cvOneD + + diff --git a/Code/Source/cvOneDOptionsJsonSerializer.h b/Code/Source/cvOneDOptionsJsonSerializer.h new file mode 100644 index 0000000..b0dd4b3 --- /dev/null +++ b/Code/Source/cvOneDOptionsJsonSerializer.h @@ -0,0 +1,49 @@ + +/* 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. + */ + +#ifndef CVONEDOPTIONSJSONSERIALIZER_H +#define CVONEDOPTIONSJSONSERIALIZER_H + +#include "cvOneDOptions.h" + +using namespace std; + +namespace cvOneD{ + +void writeJsonOptions(options const& opts, string const& fileName); + +} // namespace cvOneD + +#endif // CVONEDOPTIONSJSONSERIALIZER_H + + + diff --git a/Code/Source/cvOneDOptionsLegacySerializer.cxx b/Code/Source/cvOneDOptionsLegacySerializer.cxx new file mode 100644 index 0000000..979a204 --- /dev/null +++ b/Code/Source/cvOneDOptionsLegacySerializer.cxx @@ -0,0 +1,598 @@ +/* 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. + */ + +#include "cvOneDOptionsLegacySerializer.h" + +#include +#include +#include + +#include "cvOneDUtility.h" +#include "cvOneDGlobal.h" + +namespace cvOneD{ + +// ====================== +// READ SINGLE MODEL FILE +// ====================== +void readSingleOptionsLegacyFormat(string inputFile, options* opts, cvStringVec includedFiles){ + + // Message + printf("\n"); + printf("Reading file: %s ... \n",inputFile.c_str()); + + // Declare + cvStringVec tokenizedString; + cvLongVec tempIntVec; + string matType; + cvDoubleVec temp; + bool doInclude = false; + + // Declare input File + ifstream infile; + infile.open(inputFile); + if(infile.fail()){ + throw cvException("ERROR: Input file does not exist.\n"); + } + int lineCount = 1; + int reminder = 0; + int totSegments = 0; + + bool solverOptionDefined = false; + bool modelNameDefined = false; + + // Read Data From File + std::string buffer; + while (std::getline(infile,buffer)){ + + // Trim String + buffer = trim_string(buffer); + + // Tokenize String + tokenizedString = split_string(buffer, " ,\t"); + if (tokenizedString.size() == 0) { + continue; + } + + // Check for Empty buffer + if(!buffer.empty()){ + // CHECK THE ELEMENT TYPE + if(upper_string(tokenizedString[0]) == "MODEL"){ + //printf("Found Model.\n"); + if(modelNameDefined){ + throw cvException("ERROR: Model name already defined\n"); + } + if(tokenizedString.size() > 2){ + throw cvException(string("ERROR: Too many parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 2){ + throw cvException(string("ERROR: Not enough parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + opts->modelName = tokenizedString[1]; + }catch(...){ + throw cvException(string("ERROR: Invalid Model Name. Line " + to_string(lineCount) + "\n").c_str()); + } + modelNameDefined = true; + }else if(upper_string(tokenizedString[0]) == "NODE"){ + // printf("Found Joint.\n"); + if(tokenizedString.size() > 5){ + throw cvException(string("ERROR: Too many parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 5){ + throw cvException(string("ERROR: Not enough parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Node Name + opts->nodeName.push_back(tokenizedString[1]); + opts->nodeXcoord.push_back(atof(tokenizedString[2].c_str())); + opts->nodeYcoord.push_back(atof(tokenizedString[3].c_str())); + opts->nodeZcoord.push_back(atof(tokenizedString[4].c_str())); + }catch(...){ + throw cvException(string("ERROR: Invalid NODE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINT"){ + // printf("Found Joint.\n"); + if(tokenizedString.size() > 5){ + throw cvException(string("ERROR: Too many parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 5){ + throw cvException(string("ERROR: Not enough parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointName.push_back(tokenizedString[1]); +// opts->jointNode.push_back(atof(tokenizedString[2].c_str())); + opts->jointNode.push_back(tokenizedString[2]); + opts->jointInletName.push_back(tokenizedString[3]); + opts->jointOutletName.push_back(tokenizedString[4]); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINT Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINTINLET"){ + // printf("Found JointInlet.\n"); + if(tokenizedString.size() < 3){ + throw cvException(string("ERROR: Not enough parameters for JOINTINLET token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointInletListNames.push_back(tokenizedString[1]); + totSegments = atoi(tokenizedString[2].c_str()); + opts->jointInletListNumber.push_back(totSegments); + tempIntVec.clear(); + if(totSegments > 0){ + for(size_t loopA=3;loopAjointInletList.push_back(tempIntVec); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINTINLET Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "JOINTOUTLET"){ + // printf("Found JointOutlet.\n"); + if(tokenizedString.size() < 3){ + throw cvException(string("ERROR: Not enough parameters for JOINTOUTLET token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Get Joint Name + opts->jointOutletListNames.push_back(tokenizedString[1]); + totSegments = atoi(tokenizedString[2].c_str()); + opts->jointOutletListNumber.push_back(totSegments); + tempIntVec.clear(); + if(totSegments > 0){ + for(size_t loopA=3;loopAjointOutletList.push_back(tempIntVec); + }catch(...){ + throw cvException(string("ERROR: Invalid JOINTOUTLET Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "SEGMENT"){ + // printf("Found Segment.\n"); + if(tokenizedString.size() > 17){ + throw cvException(string("ERROR: Too many parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 17){ + throw cvException(string("ERROR: Not enough parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // char* segName, + opts->segmentName.push_back(tokenizedString[1]); + // long segID, + opts->segmentID.push_back(atoi(tokenizedString[2].c_str())); + // double segLen, + opts->segmentLength.push_back(atof(tokenizedString[3].c_str())); + // long numEls, + opts->segmentTotEls.push_back(atoi(tokenizedString[4].c_str())); + // long inNode, + opts->segmentInNode.push_back(atoi(tokenizedString[5].c_str())); + // long outNode, + opts->segmentOutNode.push_back(atoi(tokenizedString[6].c_str())); + // double InitialInletArea, + opts->segmentInInletArea.push_back(atof(tokenizedString[7].c_str())); + // double InitialOutletArea, + opts->segmentInOutletArea.push_back(atof(tokenizedString[8].c_str())); + // double InitialFlow, + opts->segmentInFlow.push_back(atof(tokenizedString[9].c_str())); + // int matID, + opts->segmentMatName.push_back(tokenizedString[10].c_str()); + // char* lossType, + opts->segmentLossType.push_back(tokenizedString[11]); + // double branchAngle, + opts->segmentBranchAngle.push_back(atof(tokenizedString[12].c_str())); + // int upstreamSegment, + opts->segmentUpstreamSegment.push_back(atoi(tokenizedString[13].c_str())); + // int branchSegment, + opts->segmentBranchSegment.push_back(atoi(tokenizedString[14].c_str())); + // char* boundType, + opts->segmentBoundType.push_back(tokenizedString[15]); + // Curve ID Instead of num,value,time + // double* value, + // double* time, + // int num + opts->segmentDataTableName.push_back(tokenizedString[16]); + }catch(...){ + throw cvException(string("ERROR: Invalid SEGMENT Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == "SOLVEROPTIONS"){ + // printf("Found Solver Options.\n"); + if(solverOptionDefined){ + throw cvException("ERROR: SOLVEROPTIONS already defined\n"); + } + if(tokenizedString.size() > 10){ + throw cvException(string("ERROR: Too many parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 10){ + throw cvException(string("ERROR: Not enough parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // double dt, + opts->timeStep = atof(tokenizedString[1].c_str()); + // long stepSize, + opts->stepSize = atof(tokenizedString[2].c_str()); + // long maxStep, + opts->maxStep = atof(tokenizedString[3].c_str()); + // long quadPoints, + opts->quadPoints = atoi(tokenizedString[4].c_str()); + // int CurveID, + opts->inletDataTableName = tokenizedString[5].c_str(); + // char* boundType, + opts->boundaryType = tokenizedString[6]; + // double conv, + opts->convergenceTolerance = atof(tokenizedString[7].c_str()); + // int useIV, + opts->useIV = atoi(tokenizedString[8].c_str()); + // int usestab + opts->useStab = atoi(tokenizedString[9].c_str()); + }catch(...){ + throw cvException(string("ERROR: Invalid SOLVEROPTIONS Format. Line " + to_string(lineCount) + "\n").c_str()); + } + solverOptionDefined = true; + }else if(upper_string(tokenizedString[0]) == std::string("OUTPUT")){ + if(tokenizedString.size() > 3){ + throw cvException(string("ERROR: Too many parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 2){ + throw cvException(string("ERROR: Not enough parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); + } + // Output Type + if(upper_string(tokenizedString[1]) == "TEXT"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_TEXT; + }else if(upper_string(tokenizedString[1]) == "VTK"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_VTK; + }else if(upper_string(tokenizedString[1]) == "BOTH"){ + cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_BOTH; + }else{ + throw cvException("ERROR: Invalid OUTPUT Type.\n"); + } + if(tokenizedString.size() > 2){ + cvOneDGlobal::vtkOutputType = atoi(tokenizedString[2].c_str()); + if(cvOneDGlobal::vtkOutputType > 1){ + throw cvException("ERROR: Invalid OUTPUT VTK Type.\n"); + } + } + }else if(upper_string(tokenizedString[0]) == std::string("DATATABLE")){ + // printf("Found Data Table.\n"); + try{ + + // Get Datatable Name + opts->dataTableName.push_back(tokenizedString[1]); + // Add the type of the datatable + opts->dataTableType.push_back(tokenizedString[2]); + + bool foundEnd = false; + temp.clear(); + while(!foundEnd){ + std::getline(infile,buffer); + lineCount++; + // Trim String + buffer = trim_string(buffer); + // Tokenize String + tokenizedString = split_string(buffer, " ,\t"); + if (tokenizedString.size() == 0) { + break; + } + // Check for Empty buffer + if(!buffer.empty()){ + if(upper_string(tokenizedString[0]) == std::string("ENDDATATABLE")){ + foundEnd = true; + }else{ + for(int loopA=0;loopAdataTableVals.push_back(temp); + }catch(...){ + throw cvException(string("ERROR: Invalid DATATABLE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if(upper_string(tokenizedString[0]) == std::string("MATERIAL")){ + // printf("Found Material.\n"); + if(tokenizedString.size() > 10){ + throw cvException(string("ERROR: Too many parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); + }else if(tokenizedString.size() < 8){ + throw cvException(string("ERROR: Not enough parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // Material Name + opts->materialName.push_back(tokenizedString[1]); + // Material Type + matType = tokenizedString[2]; + opts->materialType.push_back(matType); + // Density + opts->materialDensity.push_back(atof(tokenizedString[3].c_str())); + // Dynamic Viscosity + opts->materialViscosity.push_back(atof(tokenizedString[4].c_str())); + // Reference Pressure + opts->materialPRef.push_back(atof(tokenizedString[5].c_str())); + // Material Exponent + opts->materialExponent.push_back(atof(tokenizedString[6].c_str())); + // Extra Material Parameters + if(upper_string(matType) == "OLUFSEN"){ + opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); + opts->materialParam2.push_back(atof(tokenizedString[8].c_str())); + opts->materialParam3.push_back(atof(tokenizedString[9].c_str())); + }else if(upper_string(matType) == "LINEAR"){ + opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); + opts->materialParam2.push_back(0.0); + opts->materialParam3.push_back(0.0); + }else{ + throw cvException(string("ERROR: Invalid MATERIAL Type. Line " + to_string(lineCount) + "\n").c_str()); + } + }catch(...){ + throw cvException("ERROR: Invalid MATERIAL Format.\n"); + } + }else if(upper_string(tokenizedString[0]) == std::string("INCLUDE")){ + // Check if the file is active + if(upper_string(tokenizedString[2]) == "TRUE"){ + doInclude = true; + }else if(upper_string(tokenizedString[2]) == "FALSE"){ + doInclude = false; + }else{ + throw cvException(string("ERROR: Invalid INCLUDE switch format. Line " + to_string(lineCount) + "\n").c_str()); + } + try{ + // If active include file in list + if(doInclude){ + includedFiles.push_back(tokenizedString[1]); + } + }catch(...){ + throw cvException(string("ERROR: Invalid INCLUDE Format. Line " + to_string(lineCount) + "\n").c_str()); + } + }else if((tokenizedString.size() == 0)||(tokenizedString[0].at(0) == '#')||(tokenizedString[0].find_first_not_of(' ') == std::string::npos)){ + // printf("Found Blank.\n"); + // COMMENT OR BLANK LINE: DO NOTHING + }else{ + // TOKEN NOT RECOGNIZED + throw cvException(string("ERROR: Invalid Token in input file, line: " + to_string(lineCount) + "\n").c_str()); + } + } + // printf("Line: %d, Buffer: %s\n",lineCount,buffer.c_str()); + // getchar(); + + // Increment Line Count + lineCount++; + } + // Close File + infile.close(); +} + +void readNestedOptionsLegacyFormat(string inputFile, options* opts){ + + // List of included Files + cvStringVec includedFiles; + string currentFile; + + // Read First File + readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); + + //Read Nested Files + while(includedFiles.size() > 0){ + + // Get the first file Name + currentFile = includedFiles[0]; + // Delete the First element + includedFiles.erase(includedFiles.begin()); + // Read the file and store new included files + readSingleOptionsLegacyFormat(inputFile,opts,includedFiles); + } +} + +namespace{ + +// PRINT MODEL NAME +void printModelName(options const& opts,FILE* f){ + fprintf(f,"--- \n"); + fprintf(f,"MODEL NAME: %s\n",opts.modelName.c_str()); +} + +// PRINT NODE DATA +void printNodeData(options const& opts,FILE* f){ + fprintf(f,"--- \n"); + fprintf(f,"NODE DATA\n"); + for(long int loopA=0;loopA 0){ + fprintf(f,"LIST ITEMS\n"); + for(size_t loopB=0;loopB 0){ + fprintf(f,"LIST ITEMS\n"); + for(size_t loopB=0;loopB +#include "cvOneDOptionsJsonSerializer.h" +#include "cvOneDOptionsLegacySerializer.h" +#include "testJson.h" + using namespace std; // ==================== @@ -68,7 +72,7 @@ int getDataTableIDFromStringKey(string key){ // =============================== // CREATE MODEL AND RUN SIMULATION // =============================== -void createAndRunModel(cvOneDOptions* opts){ +void createAndRunModel(cvOneD::options* opts){ // MESSAGE printf("\n"); @@ -300,365 +304,19 @@ void createAndRunModel(cvOneDOptions* opts){ // ====================== // READ SINGLE MODEL FILE // ====================== -void readModelFile(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles){ - - // Message - printf("\n"); - printf("Reading file: %s ... \n",inputFile.c_str()); - - // Declare - cvStringVec tokenizedString; - cvLongVec tempIntVec; - string matType; - cvDoubleVec temp; - bool doInclude = false; - - // Declare input File - ifstream infile; - infile.open(inputFile); - if(infile.fail()){ - throw cvException("ERROR: Input file does not exist.\n"); - } - int lineCount = 1; - int reminder = 0; - int totSegments = 0; - - // Read Data From File - std::string buffer; - while (std::getline(infile,buffer)){ - - // Trim String - buffer = trim_string(buffer); - - // Tokenize String - tokenizedString = split_string(buffer, " ,\t"); - if (tokenizedString.size() == 0) { - continue; - } - - // Check for Empty buffer - if(!buffer.empty()){ - // CHECK THE ELEMENT TYPE - if(upper_string(tokenizedString[0]) == "MODEL"){ - //printf("Found Model.\n"); - if(opts->modelNameDefined){ - throw cvException("ERROR: Model name already defined\n"); - } - if(tokenizedString.size() > 2){ - throw cvException(string("ERROR: Too many parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 2){ - throw cvException(string("ERROR: Not enough parameters for MODEL token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - opts->modelName = tokenizedString[1]; - }catch(...){ - throw cvException(string("ERROR: Invalid Model Name. Line " + to_string(lineCount) + "\n").c_str()); - } - opts->modelNameDefined = true; - }else if(upper_string(tokenizedString[0]) == "NODE"){ - // printf("Found Joint.\n"); - if(tokenizedString.size() > 5){ - throw cvException(string("ERROR: Too many parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 5){ - throw cvException(string("ERROR: Not enough parameters for NODE token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Node Name - opts->nodeName.push_back(tokenizedString[1]); - opts->nodeXcoord.push_back(atof(tokenizedString[2].c_str())); - opts->nodeYcoord.push_back(atof(tokenizedString[3].c_str())); - opts->nodeZcoord.push_back(atof(tokenizedString[4].c_str())); - }catch(...){ - throw cvException(string("ERROR: Invalid NODE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINT"){ - // printf("Found Joint.\n"); - if(tokenizedString.size() > 5){ - throw cvException(string("ERROR: Too many parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 5){ - throw cvException(string("ERROR: Not enough parameters for JOINT token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointName.push_back(tokenizedString[1]); -// opts->jointNode.push_back(atof(tokenizedString[2].c_str())); - opts->jointNode.push_back(tokenizedString[2]); - opts->jointInletName.push_back(tokenizedString[3]); - opts->jointOutletName.push_back(tokenizedString[4]); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINT Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINTINLET"){ - // printf("Found JointInlet.\n"); - if(tokenizedString.size() < 3){ - throw cvException(string("ERROR: Not enough parameters for JOINTINLET token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointInletListNames.push_back(tokenizedString[1]); - totSegments = atoi(tokenizedString[2].c_str()); - opts->jointInletListNumber.push_back(totSegments); - tempIntVec.clear(); - if(totSegments > 0){ - for(size_t loopA=3;loopAjointInletList.push_back(tempIntVec); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINTINLET Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "JOINTOUTLET"){ - // printf("Found JointOutlet.\n"); - if(tokenizedString.size() < 3){ - throw cvException(string("ERROR: Not enough parameters for JOINTOUTLET token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Get Joint Name - opts->jointOutletListNames.push_back(tokenizedString[1]); - totSegments = atoi(tokenizedString[2].c_str()); - opts->jointOutletListNumber.push_back(totSegments); - tempIntVec.clear(); - if(totSegments > 0){ - for(size_t loopA=3;loopAjointOutletList.push_back(tempIntVec); - }catch(...){ - throw cvException(string("ERROR: Invalid JOINTOUTLET Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "SEGMENT"){ - // printf("Found Segment.\n"); - if(tokenizedString.size() > 17){ - throw cvException(string("ERROR: Too many parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 17){ - throw cvException(string("ERROR: Not enough parameters for SEGMENT token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // char* segName, - opts->segmentName.push_back(tokenizedString[1]); - // long segID, - opts->segmentID.push_back(atoi(tokenizedString[2].c_str())); - // double segLen, - opts->segmentLength.push_back(atof(tokenizedString[3].c_str())); - // long numEls, - opts->segmentTotEls.push_back(atoi(tokenizedString[4].c_str())); - // long inNode, - opts->segmentInNode.push_back(atoi(tokenizedString[5].c_str())); - // long outNode, - opts->segmentOutNode.push_back(atoi(tokenizedString[6].c_str())); - // double InitialInletArea, - opts->segmentInInletArea.push_back(atof(tokenizedString[7].c_str())); - // double InitialOutletArea, - opts->segmentInOutletArea.push_back(atof(tokenizedString[8].c_str())); - // double InitialFlow, - opts->segmentInFlow.push_back(atof(tokenizedString[9].c_str())); - // int matID, - opts->segmentMatName.push_back(tokenizedString[10].c_str()); - // char* lossType, - opts->segmentLossType.push_back(tokenizedString[11]); - // double branchAngle, - opts->segmentBranchAngle.push_back(atof(tokenizedString[12].c_str())); - // int upstreamSegment, - opts->segmentUpstreamSegment.push_back(atoi(tokenizedString[13].c_str())); - // int branchSegment, - opts->segmentBranchSegment.push_back(atoi(tokenizedString[14].c_str())); - // char* boundType, - opts->segmentBoundType.push_back(tokenizedString[15]); - // Curve ID Instead of num,value,time - // double* value, - // double* time, - // int num - opts->segmentDataTableName.push_back(tokenizedString[16]); - }catch(...){ - throw cvException(string("ERROR: Invalid SEGMENT Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == "SOLVEROPTIONS"){ - // printf("Found Solver Options.\n"); - if(opts->solverOptionDefined){ - throw cvException("ERROR: SOLVEROPTIONS already defined\n"); - } - if(tokenizedString.size() > 10){ - throw cvException(string("ERROR: Too many parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 10){ - throw cvException(string("ERROR: Not enough parameters for SOLVEROPTIONS token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // double dt, - opts->timeStep = atof(tokenizedString[1].c_str()); - // long stepSize, - opts->stepSize = atof(tokenizedString[2].c_str()); - // long maxStep, - opts->maxStep = atof(tokenizedString[3].c_str()); - // long quadPoints, - opts->quadPoints = atoi(tokenizedString[4].c_str()); - // int CurveID, - opts->inletDataTableName = tokenizedString[5].c_str(); - // char* boundType, - opts->boundaryType = tokenizedString[6]; - // double conv, - opts->convergenceTolerance = atof(tokenizedString[7].c_str()); - // int useIV, - opts->useIV = atoi(tokenizedString[8].c_str()); - // int usestab - opts->useStab = atoi(tokenizedString[9].c_str()); - }catch(...){ - throw cvException(string("ERROR: Invalid SOLVEROPTIONS Format. Line " + to_string(lineCount) + "\n").c_str()); - } - opts->solverOptionDefined = true; - }else if(upper_string(tokenizedString[0]) == std::string("OUTPUT")){ - if(tokenizedString.size() > 3){ - throw cvException(string("ERROR: Too many parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 2){ - throw cvException(string("ERROR: Not enough parameters for OUTPUT token. Line " + to_string(lineCount) + "\n").c_str()); - } - // Output Type - if(upper_string(tokenizedString[1]) == "TEXT"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_TEXT; - }else if(upper_string(tokenizedString[1]) == "VTK"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_VTK; - }else if(upper_string(tokenizedString[1]) == "BOTH"){ - cvOneDGlobal::outputType = OutputTypeScope::OUTPUT_BOTH; - }else{ - throw cvException("ERROR: Invalid OUTPUT Type.\n"); - } - if(tokenizedString.size() > 2){ - cvOneDGlobal::vtkOutputType = atoi(tokenizedString[2].c_str()); - if(cvOneDGlobal::vtkOutputType > 1){ - throw cvException("ERROR: Invalid OUTPUT VTK Type.\n"); - } - } - }else if(upper_string(tokenizedString[0]) == std::string("DATATABLE")){ - // printf("Found Data Table.\n"); - try{ - - // Get Datatable Name - opts->dataTableName.push_back(tokenizedString[1]); - // Add the type of the datatable - opts->dataTableType.push_back(tokenizedString[2]); - - bool foundEnd = false; - temp.clear(); - while(!foundEnd){ - std::getline(infile,buffer); - lineCount++; - // Trim String - buffer = trim_string(buffer); - // Tokenize String - tokenizedString = split_string(buffer, " ,\t"); - if (tokenizedString.size() == 0) { - break; - } - // Check for Empty buffer - if(!buffer.empty()){ - if(upper_string(tokenizedString[0]) == std::string("ENDDATATABLE")){ - foundEnd = true; - }else{ - for(int loopA=0;loopAdataTableVals.push_back(temp); - }catch(...){ - throw cvException(string("ERROR: Invalid DATATABLE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if(upper_string(tokenizedString[0]) == std::string("MATERIAL")){ - // printf("Found Material.\n"); - if(tokenizedString.size() > 10){ - throw cvException(string("ERROR: Too many parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); - }else if(tokenizedString.size() < 8){ - throw cvException(string("ERROR: Not enough parameters for MATERIAL token. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // Material Name - opts->materialName.push_back(tokenizedString[1]); - // Material Type - matType = tokenizedString[2]; - opts->materialType.push_back(matType); - // Density - opts->materialDensity.push_back(atof(tokenizedString[3].c_str())); - // Dynamic Viscosity - opts->materialViscosity.push_back(atof(tokenizedString[4].c_str())); - // Reference Pressure - opts->materialPRef.push_back(atof(tokenizedString[5].c_str())); - // Material Exponent - opts->materialExponent.push_back(atof(tokenizedString[6].c_str())); - // Extra Material Parameters - if(upper_string(matType) == "OLUFSEN"){ - opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); - opts->materialParam2.push_back(atof(tokenizedString[8].c_str())); - opts->materialParam3.push_back(atof(tokenizedString[9].c_str())); - }else if(upper_string(matType) == "LINEAR"){ - opts->materialParam1.push_back(atof(tokenizedString[7].c_str())); - opts->materialParam2.push_back(0.0); - opts->materialParam3.push_back(0.0); - }else{ - throw cvException(string("ERROR: Invalid MATERIAL Type. Line " + to_string(lineCount) + "\n").c_str()); - } - }catch(...){ - throw cvException("ERROR: Invalid MATERIAL Format.\n"); - } - }else if(upper_string(tokenizedString[0]) == std::string("INCLUDE")){ - // Check if the file is active - if(upper_string(tokenizedString[2]) == "TRUE"){ - doInclude = true; - }else if(upper_string(tokenizedString[2]) == "FALSE"){ - doInclude = false; - }else{ - throw cvException(string("ERROR: Invalid INCLUDE switch format. Line " + to_string(lineCount) + "\n").c_str()); - } - try{ - // If active include file in list - if(doInclude){ - includedFiles.push_back(tokenizedString[1]); - } - }catch(...){ - throw cvException(string("ERROR: Invalid INCLUDE Format. Line " + to_string(lineCount) + "\n").c_str()); - } - }else if((tokenizedString.size() == 0)||(tokenizedString[0].at(0) == '#')||(tokenizedString[0].find_first_not_of(' ') == std::string::npos)){ - // printf("Found Blank.\n"); - // COMMENT OR BLANK LINE: DO NOTHING - }else{ - // TOKEN NOT RECOGNIZED - throw cvException(string("ERROR: Invalid Token in input file, line: " + to_string(lineCount) + "\n").c_str()); - } - } - // printf("Line: %d, Buffer: %s\n",lineCount,buffer.c_str()); - // getchar(); - // Increment Line Count - lineCount++; - } - // Close File - infile.close(); +// Retain legacy implementation to maintain the python interface +void readModelFile(string inputFile, cvOneD::options* opts, cvStringVec includedFiles){ + readSingleOptionsLegacyFormat(inputFile, opts, includedFiles); } // ==================== // READ MODEL FROM FILE // ==================== -void readModel(string inputFile, cvOneDOptions* opts){ - - // List of included Files - cvStringVec includedFiles; - string currentFile; - - // Read First File - readModelFile(inputFile,opts,includedFiles); - //Read Nested Files - while(includedFiles.size() > 0){ - - // Get the first file Name - currentFile = includedFiles[0]; - // Delete the First element - includedFiles.erase(includedFiles.begin()); - // Read the file and store new included files - readModelFile(inputFile,opts,includedFiles); - } +// Retain legacy implementation to maintain the python interface +void readModel(string inputFile, cvOneD::options* opts){ + readNestedOptionsLegacyFormat(inputFile, opts); } // ============== @@ -667,17 +325,17 @@ void readModel(string inputFile, cvOneDOptions* opts){ void runOneDSolver(string inputFile){ // Create Options - cvOneDOptions* opts = new cvOneDOptions(); + cvOneD::options* opts = new cvOneD::options(); // Read Model From File readModel(inputFile,opts); // Model Checking - opts->check(); + cvOneD::validateOptions(*opts); // Print Input Data Echo string fileName("echo.out"); - opts->printToFile(fileName); + cvOneD::printToLegacyFile(*opts,fileName); // Create Model and Run Simulation createAndRunModel(opts); @@ -691,6 +349,14 @@ void runOneDSolver(string inputFile){ // ============= int main(int argc, char** argv){ + // **TEMPORARY** This is a temporary check for the + // json serializer/deserializer. + // Obviously, this will need to be + // made robust and moved to an actual + // test executable. + test_json(); + return 0; + // Write Program Header WriteHeader(); diff --git a/Code/Source/main.h b/Code/Source/main.h index edf0712..4f27540 100644 --- a/Code/Source/main.h +++ b/Code/Source/main.h @@ -44,8 +44,8 @@ using namespace std; void WriteHeader(); int getDataTableIDFromStringKey(string key); -void createAndRunModel(cvOneDOptions* opts); -void readModelFile(string inputFile, cvOneDOptions* opts, cvStringVec includedFiles); -void readModel(string inputFile, cvOneDOptions* opts); +void createAndRunModel(cvOneD::options* opts); +void readModelFile(string inputFile, cvOneD::options* opts, cvStringVec includedFiles); +void readModel(string inputFile, cvOneD::options* opts); void runOneDSolver(string inputFile); diff --git a/Code/Source/testJson.cxx b/Code/Source/testJson.cxx new file mode 100644 index 0000000..e50cea7 --- /dev/null +++ b/Code/Source/testJson.cxx @@ -0,0 +1,89 @@ + +#include "testJson.h" + +#include +#include +#include + +// Test function to demonstrate the serialization +void test_json(){ + cvOneD::options opts; + + opts.modelName = "MyModel"; + opts.nodeName = {"Node1", "Node2", "Node3"}; + opts.nodeXcoord = {1.1, 2.2, 3.3}; + opts.nodeYcoord = {4.4, 5.5, 6.6}; + opts.nodeZcoord = {7.7, 8.8, 9.9}; + + // Joint Data + opts.jointName = {"Joint1", "Joint2", "Joint3"}; + // We would also like to include "jointNode" here + // once it is properly integrated in all existing + // input files. + opts.jointInletName = {"Inlet1", "Inlet2", "Inlet3"}; + opts.jointOutletName = {"Outlet1", "Outlet2", "Outlet3"}; + + opts.jointInletListNames = {"IN1", "IN2", "IN3"}; + opts.jointInletListNumber = {1, 1, 1}; + opts.jointInletList = {{0}, {1}, {2}}; + + opts.jointOutletListNames = {"OUT1", "OUT2", "OUT3"}; + opts.jointOutletListNumber = {1, 1, 1}; + opts.jointOutletList = {{1}, {2}, {3}}; + + // Material Data + opts.materialName = {"Material1", "Material2"}; + opts.materialType = {"Type1", "Type2"}; + opts.materialDensity = {1000.0, 1200.0}; + opts.materialViscosity = {0.3, 0.4}; + opts.materialPRef = {1.0, 1.2}; + opts.materialExponent = {0.5, 0.6}; + opts.materialParam1 = {10.0, 20.0}; + opts.materialParam2 = {30.0, 40.0}; + opts.materialParam3 = {50.0, 60.0}; + + // Data tables + opts.dataTableName = {"R_VALS", "STEADY_FLOW", "PULS_FLOW"}; + opts.dataTableType = {"LIST", "LIST", "LIST"}; + opts.dataTableVals = { + {0.0, 991.36}, + {0.0, 7.985, 1.0, 7.985}, + {0.0, 0.0, 0.019668108360095, -4.11549971450822, 0.055247073448669, -7.16517105402019} + }; + + // Segment Data + opts.segmentName = {"Aorta", "iliacR", "iliacL"}; + opts.segmentID = {0, 1, 2}; + opts.segmentLength = {17.670671, 12.997461, 12.997461}; + opts.segmentTotEls = {50, 50, 50}; + opts.segmentInNode = {0, 1, 1}; + opts.segmentOutNode = {1, 3, 2}; + opts.segmentInInletArea = {5.027254990390394, 1.55, 1.55}; + opts.segmentInOutletArea = {1.6068894493599328, 0.3525652531134944, 0.3525652531134944}; + opts.segmentInFlow = {0.0, 0.0, 0.0}; + opts.segmentMatName = {"MAT1", "MAT1", "MAT1"}; + opts.segmentLossType = {"NONE", "NONE", "NONE"}; + opts.segmentBranchAngle = {0.0, 0.0, 0.0}; + opts.segmentUpstreamSegment = {0, 0, 0}; + opts.segmentBranchSegment = {0, 0, 0}; + opts.segmentBoundType = {"NOBOUND", "RCR", "RCR"}; + opts.segmentDataTableName = {"NONE", "RCR_VALS", "RCR_VALS"}; + + // Solver Options + opts.timeStep = 0.001087; + opts.stepSize = 10; + opts.maxStep = 100; + opts.quadPoints = 2; + opts.inletDataTableName = "STEADY_FLOW"; + opts.boundaryType = "FLOW"; + opts.convergenceTolerance = 1.0e-6; + opts.useIV = 1; + opts.useStab = 0; + opts.outputType = "NONE"; + + // Serialize the options into a JSON object + cvOneD::writeJsonOptions(opts,"out.json"); + + cvOneD::readJsonOptions("out.json"); + +} \ No newline at end of file diff --git a/Code/Source/testJson.h b/Code/Source/testJson.h new file mode 100644 index 0000000..5a2cdbb --- /dev/null +++ b/Code/Source/testJson.h @@ -0,0 +1,6 @@ + +#include "cvOneDOptions.h" +#include "cvOneDOptionsJsonParser.h" +#include "cvOneDOptionsJsonSerializer.h" + +void test_json(); \ No newline at end of file From 54a60264a7527a03fdfd6e38387828603aef3743 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Mon, 17 Feb 2025 09:35:22 -0500 Subject: [PATCH 2/4] add sstream include --- Code/Source/cvOneDOptionsJsonParser.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Source/cvOneDOptionsJsonParser.cxx b/Code/Source/cvOneDOptionsJsonParser.cxx index 5dc568d..a165af8 100644 --- a/Code/Source/cvOneDOptionsJsonParser.cxx +++ b/Code/Source/cvOneDOptionsJsonParser.cxx @@ -1,5 +1,7 @@ #include #include +#include + #include #include "cvOneDOptionsJsonSerializer.h" From 0d56b52f640f9e9ffd415c387e87594bb04e7007 Mon Sep 17 00:00:00 2001 From: arsLibera Date: Mon, 17 Feb 2025 09:41:21 -0500 Subject: [PATCH 3/4] remove main.h --- Code/Source/main.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Code/Source/main.h b/Code/Source/main.h index 4f27540..095f0d7 100644 --- a/Code/Source/main.h +++ b/Code/Source/main.h @@ -40,12 +40,4 @@ # include "cvOneDDataTable.h" # include "cvOneDException.h" -using namespace std; - -void WriteHeader(); -int getDataTableIDFromStringKey(string key); -void createAndRunModel(cvOneD::options* opts); -void readModelFile(string inputFile, cvOneD::options* opts, cvStringVec includedFiles); -void readModel(string inputFile, cvOneD::options* opts); -void runOneDSolver(string inputFile); From a6f8b18b3bd5277cf8ea7cb95693478ee094377a Mon Sep 17 00:00:00 2001 From: arsLibera Date: Mon, 17 Feb 2025 09:44:44 -0500 Subject: [PATCH 4/4] update comments --- Code/Source/main.cxx | 16 +++++++++------- Code/Source/main.h | 43 ------------------------------------------- 2 files changed, 9 insertions(+), 50 deletions(-) delete mode 100644 Code/Source/main.h diff --git a/Code/Source/main.cxx b/Code/Source/main.cxx index 4e81c0f..26e30f0 100644 --- a/Code/Source/main.cxx +++ b/Code/Source/main.cxx @@ -29,10 +29,12 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "main.h" #include #include +#include "cvOneDGlobal.h" +#include "cvOneDModelManager.h" +#include "cvOneDOptions.h" #include "cvOneDOptionsJsonParser.h" #include "cvOneDOptionsJsonSerializer.h" #include "cvOneDOptionsLegacySerializer.h" @@ -403,9 +405,7 @@ std::optional parseArgsAndHandleOptions(int argc, char** argv){ *argOptions.jsonConversionOutput); } - // We're requiring users to specify a JSON input file in - // the new behavior. Otherwise, there's no point to - // running the program at all. + // Only return the input args if the user provided them if(argOptions.jsonInput){ return cvOneD::readJsonOptions(*argOptions.jsonInput); } @@ -423,12 +423,14 @@ int main(int argc, char** argv){ try{ - // Eventually, simulationOptions should be const auto const simulationOptions = parseArgsAndHandleOptions(argc,argv); if(simulationOptions){ + // The simulation options were defined so we can run the simulation runOneDSolver(*simulationOptions); - } - else{ + } else{ + // The user could just want to convert legacy input *.in -> *.json + // so we don't error but we notify the user that no simulation + // is run. std::cout << "The simulation was not run because" " no input file was provided." << std::endl; } diff --git a/Code/Source/main.h b/Code/Source/main.h deleted file mode 100644 index 095f0d7..0000000 --- a/Code/Source/main.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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. - */ - -# include -# include -# include - -# include "cvOneDGlobal.h" -# include "cvOneDUtility.h" -# include "cvOneDModelManager.h" -# include "cvOneDOptions.h" -# include "cvOneDDataTable.h" -# include "cvOneDException.h" - -