diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp index f3ce605e38..e39f6aeb42 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.cpp @@ -53,12 +53,6 @@ RimModeledWellPath::RimModeledWellPath() CAF_PDM_InitScriptableFieldWithScriptKeywordNoDefault( &m_geometryDefinition, "WellPathGeometryDef", "WellPathGeometry", "Trajectory" ); m_geometryDefinition = new RimWellPathGeometryDef; m_geometryDefinition->changed.connect( this, &RimModeledWellPath::onGeometryDefinitionChanged ); - - // Required, as these settings are set in RimWellPath() - m_name.uiCapability()->setUiReadOnly( false ); - m_name.uiCapability()->setUiHidden( false ); - m_name.xmlCapability()->setIOReadable( true ); - m_name.xmlCapability()->setIOWritable( true ); } //-------------------------------------------------------------------------------------------------- @@ -183,16 +177,6 @@ void RimModeledWellPath::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrd RimWellPath::defineUiTreeOrdering( uiTreeOrdering, uiConfigName ); } -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void RimModeledWellPath::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) -{ - uiOrdering.add( &m_name ); - - RimWellPath::defineUiOrdering( uiConfigName, uiOrdering ); -} - //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.h index 00bb985124..141467bab6 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimModeledWellPath.h @@ -44,7 +44,6 @@ public: private: void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName ) override; - void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; void onGeometryDefinitionChanged( const caf::SignalEmitter* emitter, bool fullUpdate ); QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPath.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPath.cpp index 2543a18a92..ecf3c8b8ce 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPath.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPath.cpp @@ -23,12 +23,6 @@ RimOsduWellPath::RimOsduWellPath() CAF_PDM_InitField( &m_datumElevationFromOsdu, "DatumElevationFromOsdu", 0.0, "Datum Elevation From OSDU" ); m_datumElevationFromOsdu.uiCapability()->setUiReadOnly( true ); - - // Required, as these settings are set in RimWellPath() - m_name.uiCapability()->setUiReadOnly( false ); - m_name.uiCapability()->setUiHidden( false ); - m_name.xmlCapability()->setIOReadable( true ); - m_name.xmlCapability()->setIOWritable( true ); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp index 837cb1c514..41bc43efc0 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPath.cpp @@ -97,9 +97,6 @@ RimWellPath::RimWellPath() CAF_PDM_InitScriptableFieldNoDefault( &m_name, "Name", "Name" ); m_name.registerKeywordAlias( "WellPathName" ); - m_name.uiCapability()->setUiReadOnly( true ); - m_name.uiCapability()->setUiHidden( true ); - m_name.xmlCapability()->disableIO(); CAF_PDM_InitFieldNoDefault( &m_airGap, "AirGap", "Air Gap" ); m_airGap.registerGetMethod( this, &RimWellPath::airGap ); @@ -711,6 +708,8 @@ void RimWellPath::setWellPathGeometry( RigWellPath* wellPathModel ) //-------------------------------------------------------------------------------------------------- void RimWellPath::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) { + uiOrdering.add( &m_name ); + if ( m_wellPathTieIn() ) { m_wellPathTieIn->uiOrdering( uiConfigName, uiOrdering ); diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp index ba892456af..743524929f 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp @@ -250,24 +250,26 @@ std::vector RimWellPathCollection::addWellPaths( QStringList fileP { // Check if this file is already open bool alreadyOpen = false; - for ( const auto& wellPath : m_wellPaths ) - { - auto* fWPath = dynamic_cast( wellPath.p() ); - if ( !fWPath ) continue; - QFile f1; - f1.setFileName( filePath ); - QString s1 = f1.fileName(); - QFile f2; - f2.setFileName( fWPath->filePath() ); - QString s2 = f2.fileName(); - if ( s1 == s2 ) + if ( filePath.endsWith( "json", Qt::CaseInsensitive ) ) + { + for ( const auto& wellPath : m_wellPaths ) { - // printf("Attempting to open well path JSON file that is already open:\n %s\n", (const char*) - // filePath.toLocal8Bit()); - alreadyOpen = true; - errorMessages->push_back( QString( "%1 is already loaded" ).arg( filePath ) ); - break; + auto* fWPath = dynamic_cast( wellPath.p() ); + if ( !fWPath ) continue; + + QFile f1; + f1.setFileName( filePath ); + QString s1 = f1.fileName(); + QFile f2; + f2.setFileName( fWPath->filePath() ); + QString s2 = f2.fileName(); + if ( s1 == s2 ) + { + alreadyOpen = true; + errorMessages->push_back( QString( "%1 is already loaded" ).arg( filePath ) ); + break; + } } } @@ -343,7 +345,7 @@ void RimWellPathCollection::readAndAddWellPaths( std::vector& // NB! Do not use tryFindMatchingWellPath(), as this function will remove the prefix and will return an false // match in many cases. auto* existingWellPath = dynamic_cast( wellPathByName( wellPath->name() ) ); - if ( existingWellPath ) + if ( existingWellPath && !existingWellPath->filePath().endsWith( ".dev" ) ) { existingWellPath->setFilepath( wellPath->filePath() ); existingWellPath->setWellPathIndexInFile( wellPath->wellPathIndexInFile() ); diff --git a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake index d91e9320fd..badf6c31a8 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/ProjectDataModelCommands/CMakeLists_files.cmake @@ -19,6 +19,7 @@ set(SOURCE_GROUP_HEADER_FILES ${CMAKE_CURRENT_LIST_DIR}/RimcWellPathGeometryDef.h ${CMAKE_CURRENT_LIST_DIR}/RimcModeledWellPath.h ${CMAKE_CURRENT_LIST_DIR}/RimcWellPath.h + ${CMAKE_CURRENT_LIST_DIR}/RimcWellPathCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplateCollection.h ${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplate.h ${CMAKE_CURRENT_LIST_DIR}/RimcThermalFractureTemplate.h @@ -53,6 +54,7 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RimcWellPathGeometryDef.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcModeledWellPath.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcWellPath.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimcWellPathCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplateCollection.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcFractureTemplate.cpp ${CMAKE_CURRENT_LIST_DIR}/RimcThermalFractureTemplate.cpp diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp index 0e4de80644..3709436e35 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.cpp @@ -36,6 +36,8 @@ #include "RimProject.h" #include "RimSummaryCase.h" #include "RimSurfaceCollection.h" +#include "RimTools.h" +#include "RimWellPathCollection.h" #include "RiuMainWindow.h" #include "RiuPlotMainWindow.h" @@ -284,3 +286,36 @@ QString RimProject_createGridFromKeyValues::classKeywordReturnedType() const { return RimCornerPointCase::classKeywordStatic(); } + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimProject, RimProject_wellPathCollection, "wellPathCollection" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimProject_wellPathCollection::RimProject_wellPathCollection( caf::PdmObjectHandle* self ) + : PdmObjectMethod( self, PdmObjectMethod::NullPointerType::NULL_IS_INVALID, PdmObjectMethod::ResultType::PERSISTENT_TRUE ) +{ + CAF_PDM_InitObject( "Get Well Path Collection", "", "", "Get Well Path Collection" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimProject_wellPathCollection::execute() +{ + auto wellPathCollection = RimTools::wellPathCollection(); + if ( !wellPathCollection ) + { + return std::unexpected( "No well path collection found." ); + } + + return wellPathCollection; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimProject_wellPathCollection::classKeywordReturnedType() const +{ + return RimWellPathCollection::classKeywordStatic(); +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h index 362cf90187..54580394cf 100644 --- a/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcProject.h @@ -99,3 +99,17 @@ private: caf::PdmField m_zcornKey; caf::PdmField m_actnumKey; }; + +//================================================================================================== +/// +//================================================================================================== +class RimProject_wellPathCollection : public caf::PdmObjectMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimProject_wellPathCollection( caf::PdmObjectHandle* self ); + + std::expected execute() override; + QString classKeywordReturnedType() const override; +}; diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.cpp b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.cpp new file mode 100644 index 0000000000..ef58ab77c6 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.cpp @@ -0,0 +1,79 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2025 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RimcWellPathCollection.h" + +#include "RimWellPath.h" +#include "RimWellPathCollection.h" + +#include "WellPathCommands/RicImportWellPaths.h" + +#include "cafPdmFieldScriptingCapability.h" + +CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellPathCollection, RimcWellPathCollection_importWellPath, "ImportWellPath" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimcWellPathCollection_importWellPath::RimcWellPathCollection_importWellPath( caf::PdmObjectHandle* self ) + : caf::PdmObjectCreationMethod( self ) +{ + CAF_PDM_InitObject( "Import Well Path" ); + CAF_PDM_InitScriptableFieldNoDefault( &m_fileName, "FileName", "File Name" ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::expected RimcWellPathCollection_importWellPath::execute() +{ + auto wellPathCollection = self(); + if ( !wellPathCollection ) + { + return std::unexpected( QString( "Well path collection is null. Cannot add well path." ) ); + } + + if ( m_fileName().isEmpty() ) + { + return std::unexpected( QString( "File name is empty. Cannot add well path." ) ); + } + + QStringList errorMessages; + std::vector importedWellPaths = wellPathCollection->addWellPaths( { m_fileName() }, &errorMessages ); + if ( importedWellPaths.empty() ) + { + if ( !errorMessages.empty() ) + { + return std::unexpected( errorMessages.join( "\n" ) ); + } + else + { + return std::unexpected( QString( "No well paths were imported from file '%1'." ).arg( m_fileName() ) ); + } + } + + return importedWellPaths.front(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RimcWellPathCollection_importWellPath::classKeywordReturnedType() const +{ + return RimWellPath::classKeywordStatic(); +} diff --git a/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.h b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.h new file mode 100644 index 0000000000..e78e1b0642 --- /dev/null +++ b/ApplicationLibCode/ProjectDataModelCommands/RimcWellPathCollection.h @@ -0,0 +1,42 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2025 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafPdmField.h" +#include "cafPdmObjectHandle.h" +#include "cafPdmObjectMethod.h" + +#include + +//================================================================================================== +/// +//================================================================================================== +class RimcWellPathCollection_importWellPath : public caf::PdmObjectCreationMethod +{ + CAF_PDM_HEADER_INIT; + +public: + RimcWellPathCollection_importWellPath( caf::PdmObjectHandle* self ); + + std::expected execute() override; + QString classKeywordReturnedType() const override; + +private: + caf::PdmField m_fileName; +}; diff --git a/GrpcInterface/Python/rips/PythonExamples/surfaces_and_visualization/create_intersection.py b/GrpcInterface/Python/rips/PythonExamples/surfaces_and_visualization/create_intersection.py index cc7b3bca8e..69a56ecae3 100644 --- a/GrpcInterface/Python/rips/PythonExamples/surfaces_and_visualization/create_intersection.py +++ b/GrpcInterface/Python/rips/PythonExamples/surfaces_and_visualization/create_intersection.py @@ -23,7 +23,7 @@ intersection.points = [ intersection.update() # Add a new modeled well path -well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path_coll = resinsight.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "Test Well-1" well_path.update() diff --git a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/fishbones_completion.py b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/fishbones_completion.py index 38396a8830..99610f515f 100644 --- a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/fishbones_completion.py +++ b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/fishbones_completion.py @@ -8,7 +8,7 @@ resinsight = rips.Instance.find() # The coordinates are based on the Norne case # Add a lateral to the main well path -well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path_coll = resinsight.project.well_path_collection() editable_well_paths = well_path_coll.descendants(rips.ModeledWellPath) if editable_well_paths: diff --git a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path.py b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path.py index 74af16e9af..32cf81405d 100644 --- a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path.py +++ b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path.py @@ -7,7 +7,7 @@ resinsight = rips.Instance.find() # Create a modeled well path and add well path targets # The coordinates are based on the Norne case -well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path_coll = resinsight.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "Test Well-1" well_path.update() diff --git a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path_lateral.py b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path_lateral.py index 3200a2156c..c91407614c 100644 --- a/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path_lateral.py +++ b/GrpcInterface/Python/rips/PythonExamples/wells_and_fractures/modeled_well_path_lateral.py @@ -9,7 +9,7 @@ resinsight = rips.Instance.find() # The coordinates are based on the Norne case # Add a lateral to the main well path -well_path_coll = resinsight.project.descendants(rips.WellPathCollection)[0] +well_path_coll = resinsight.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "Test Well-1" well_path.update() diff --git a/GrpcInterface/Python/rips/tests/test_create_well_path.py b/GrpcInterface/Python/rips/tests/test_create_well_path.py index cde7cb6095..a8ef97a1c0 100644 --- a/GrpcInterface/Python/rips/tests/test_create_well_path.py +++ b/GrpcInterface/Python/rips/tests/test_create_well_path.py @@ -6,7 +6,7 @@ import rips def test_add_new_object_for_well_paths(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() my_well_path = well_path_coll.add_new_object(rips.ModeledWellPath) my_well_path.name = "test" @@ -30,7 +30,7 @@ def test_add_new_object_for_well_paths(rips_instance, initialize_test): def test_add_well_path_targets(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() my_well_path = well_path_coll.add_new_object(rips.ModeledWellPath) my_well_path.name = "test" @@ -61,7 +61,7 @@ def test_add_well_path_targets(rips_instance, initialize_test): def test_duplicate_well_path(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() my_well_path = well_path_coll.add_new_object(rips.ModeledWellPath) my_well_path.name = "test" diff --git a/GrpcInterface/Python/rips/tests/test_object_lifetime.py b/GrpcInterface/Python/rips/tests/test_object_lifetime.py index 8a157d9306..2aa318fa62 100644 --- a/GrpcInterface/Python/rips/tests/test_object_lifetime.py +++ b/GrpcInterface/Python/rips/tests/test_object_lifetime.py @@ -6,7 +6,7 @@ import rips def test_well_path(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() assert len(well_path_coll.well_paths()) == 0 well_path = well_path_coll.add_new_object(rips.ModeledWellPath) diff --git a/GrpcInterface/Python/rips/tests/test_wells_path_completions.py b/GrpcInterface/Python/rips/tests/test_wells_path_completions.py index ea1fbf7f33..61746003e8 100644 --- a/GrpcInterface/Python/rips/tests/test_wells_path_completions.py +++ b/GrpcInterface/Python/rips/tests/test_wells_path_completions.py @@ -26,7 +26,7 @@ def test_10k(rips_instance, initialize_test): def test_add_well_path_completions(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "test" @@ -144,7 +144,7 @@ def test_add_well_path_fracture_template(rips_instance, initialize_test): # test fishbone interface def test_fishbone_interface(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "fishbone_well" @@ -194,7 +194,7 @@ def test_fishbone_interface(rips_instance, initialize_test): # test perforations and perforations settings def test_perforation_settings(rips_instance, initialize_test): - well_path_coll = rips_instance.project.descendants(rips.WellPathCollection)[0] + well_path_coll = rips_instance.project.well_path_collection() well_path = well_path_coll.add_new_object(rips.ModeledWellPath) well_path.name = "perforated_well"