#14001 Python: Drop hand-written PropertyType, use auto-generated PropertyType

Consolidate the hand-written PropertyType / NNCPropertyType enums in
enums.py into the auto-generated StrEnum classes that already exist in
generated_classes.py. enums.py and all its imports are removed.

The auto-generated class for caf::AppEnum<RiaDefines::ResultCatType>
was named "ResultType" because the existing scriptable m_resultType
fields used keyword "ResultType". Switch those fields to
CAF_PDM_InitScriptableFieldWithScriptKeywordNoDefault, keeping the XML
keyword "ResultType" (so project files still load) but exposing the
script keyword as "PropertyType". The generator now emits a single
PropertyType StrEnum that:

- matches the proto PropertyType for setters (FLOAT/INTEGER aside, the
  members callers actually use are identical),
- gains ALLAN_DIAGRAMS that the C++ side already knew about,
- loses the proto-only REMOVED/UNDEFINED sentinels (unused in any
  Python caller).

rips.NNCPropertyType was exported but unreferenced anywhere — dropped.

Updated Python callers:
- rips.ResultType.X -> rips.PropertyType.X across examples and tests.
- result_type= kwarg / .result_type attribute -> property_type /
  .property_type (the script keyword change renamed those everywhere
  the affected PdmObject fields are accessed from Python).
- view.apply_cell_result's hand-written wrapper now takes property_type
  instead of result_type and forwards via cell_result.property_type.

All 271 Python tests pass, ruff and clang-format clean.
This commit is contained in:
Kristian Bendiksen committed 2026-05-26 08:23:28 +02:00
1 parent a6c3ac2d1a
commit 01ceb57323
19 files changed
+126 -71

No files matched your search

@@ -23,6 +23,7 @@
#include "RiaResultNames.h"
#include "cafAppEnum.h"
#include "cafPdmScriptEnumNameRegistry.h"
namespace caf
{
@@ -179,6 +180,19 @@ void caf::AppEnum<RiaDefines::RowCount>::setUp()
} // namespace caf
namespace
{
// Pin the Python StrEnum class name for caf::AppEnum<RiaDefines::ResultCatType> to "PropertyType".
// Without this override the generator derives the class name from the first field's script keyword
// ("ResultType"), which both diverges from the proto PropertyType name and shifts whenever class
// iteration order changes.
struct RegisterScriptEnumNames
{
RegisterScriptEnumNames() { caf::PdmScriptEnumNameRegistry::registerName<RiaDefines::ResultCatType>( "PropertyType" ); }
};
const RegisterScriptEnumNames s_registerScriptEnumNames;
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -16,6 +16,7 @@ set(PROJECT_FILES
cafPdmDataContainerEnum.cpp
cafPdmObjectMethod.h
cafPdmObjectMethod.cpp
cafPdmScriptEnumNameRegistry.h
cafPdmObjectScriptingCapabilityRegister.h
cafPdmObjectScriptingCapabilityRegister.cpp
cafPdmPythonGenerator.h
@@ -45,6 +45,7 @@
#include "cafPdmObjectMethod.h"
#include "cafPdmObjectScriptingCapabilityRegister.h"
#include "cafPdmProxyValueField.h"
#include "cafPdmScriptEnumNameRegistry.h"
#include "cafPdmXmlFieldHandle.h"
#ifndef CAF_EXCLUDE_CVF
@@ -216,7 +217,10 @@ QString caf::PdmPythonGenerator::generate( PdmObjectFactory* factory, std::vecto
auto cached = enumClassNameByDataTypeName.find( dataTypeName );
if ( cached != enumClassNameByDataTypeName.end() ) return cached->second;
QString baseName = snakeToCamelCase( camelToSnakeCase( scriptability->scriptFieldName() ) );
// Prefer an explicit override registered by application code so the Python enum class name
// is stable independent of which field the generator visits first.
QString baseName = PdmScriptEnumNameRegistry::lookup( dataTypeName );
if ( baseName.isEmpty() ) baseName = snakeToCamelCase( camelToSnakeCase( scriptability->scriptFieldName() ) );
if ( baseName.isEmpty() ) baseName = "Enum";
QString candidate = baseName;
@@ -0,0 +1,79 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2026- Equinor ASA
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library 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.
//
// This library 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 <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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 Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#pragma once
#include "cafAppEnum.h"
#include <QString>
#include <map>
#include <typeinfo>
namespace caf
{
//==================================================================================================
/// Optional override for the Python StrEnum class name the code generator emits for a given
/// caf::AppEnum<T>. Without an override, the generator derives the class name from the script
/// keyword of the first field it visits — which is sensitive to class/iteration order. Use this
/// registry to pin a canonical Python name for an AppEnum, independent of any field's script
/// keyword.
//==================================================================================================
class PdmScriptEnumNameRegistry
{
public:
template <typename EnumType>
static void registerName( const QString& scriptClassName )
{
registry()[QString::fromLatin1( typeid( caf::AppEnum<EnumType> ).name() )] = scriptClassName;
}
static QString lookup( const QString& dataTypeName )
{
const auto& m = registry();
auto it = m.find( dataTypeName );
if ( it == m.end() ) return QString();
return it->second;
}
private:
static std::map<QString, QString>& registry()
{
static std::map<QString, QString> s_map;
return s_map;
}
};
} // namespace caf
@@ -21,4 +21,4 @@ case_group = resinsight.project.create_grid_case_group(case_paths=case_paths)
case_group.compute_statistics()
view = case_group.views()[0]
view.apply_cell_result(rips.ResultType.DYNAMIC_NATIVE, "PRESSURE_DEV")
view.apply_cell_result(rips.PropertyType.DYNAMIC_NATIVE, "PRESSURE_DEV")
@@ -45,4 +45,4 @@ case.set_active_cell_property_async(
end = time.time()
print("Time elapsed: ", end - start)
print("Transferred all results back")
view = case.views()[0].apply_cell_result(rips.ResultType.GENERATED, "POROPERMXAS")
view = case.views()[0].apply_cell_result(rips.PropertyType.GENERATED, "POROPERMXAS")
@@ -97,6 +97,6 @@ print(f" Unassigned (no polygon): {unassigned} cells")
# Apply the generated result in the view to visualize the regions
view = case.views()[0] if case.views() else case.create_view()
view.apply_cell_result(
result_type=rips.ResultType.GENERATED, result_variable=property_name
result_type=rips.PropertyType.GENERATED, result_variable=property_name
)
print(f"Applied '{property_name}' cell result to view")
@@ -6,4 +6,6 @@ import rips
resinsight = rips.Instance.find()
view = resinsight.project.views()[0]
view.apply_cell_result(result_type=rips.ResultType.STATIC_NATIVE, result_variable="DX")
view.apply_cell_result(
result_type=rips.PropertyType.STATIC_NATIVE, result_variable="DX"
)
@@ -51,4 +51,4 @@ print("Time elapsed: ", end - start)
print("Transferred all results back")
view = case.views()[0].apply_cell_result(rips.ResultType.GENERATED, "SOILPORVAsync")
view = case.views()[0].apply_cell_result(rips.PropertyType.GENERATED, "SOILPORVAsync")
@@ -34,4 +34,4 @@ print("Time elapsed: ", end - start)
print("Transferred all results back")
view = case.views()[0].apply_cell_result(rips.ResultType.GENERATED, "SOILPORVSync")
view = case.views()[0].apply_cell_result(rips.PropertyType.GENERATED, "SOILPORVSync")
@@ -38,7 +38,7 @@ for case in cases:
for view in case.views():
for property in property_list:
view.apply_cell_result(
result_type=rips.ResultType.DYNAMIC_NATIVE, result_variable=property
result_type=rips.PropertyType.DYNAMIC_NATIVE, result_variable=property
)
for time_step in range(0, len(time_steps), 10):
view.set_time_step(time_step=time_step)
@@ -27,7 +27,7 @@ view1.set_time_step(time_step=2)
# Set cell result to SOIL
view1.apply_cell_result(
result_type=rips.ResultType.DYNAMIC_NATIVE, result_variable="SOIL"
result_type=rips.PropertyType.DYNAMIC_NATIVE, result_variable="SOIL"
)
@@ -28,7 +28,7 @@ view1.set_time_step(time_step=2)
# Set cell result to SOIL
view1.apply_cell_result(
result_type=rips.ResultType.DYNAMIC_NATIVE, result_variable="SOIL"
result_type=rips.PropertyType.DYNAMIC_NATIVE, result_variable="SOIL"
)
# Set export folder for snapshots and properties
@@ -45,14 +45,14 @@ print("Overburden: ", stim_plan_model_template.overburden_formation)
# Set eclipse result for facies definition
eclipse_result = stim_plan_model_template.facies_properties().facies_definition()
eclipse_result.result_type = rips.ResultType.INPUT_PROPERTY
eclipse_result.result_type = rips.PropertyType.INPUT_PROPERTY
eclipse_result.result_variable = "OPERNUM_1"
eclipse_result.update()
# Set eclipse result for non-net layers
non_net_layers = stim_plan_model_template.non_net_layers()
non_net_layers_result = non_net_layers.facies_definition()
non_net_layers_result.result_type = rips.ResultType.STATIC_NATIVE
non_net_layers_result.result_type = rips.PropertyType.STATIC_NATIVE
non_net_layers_result.result_variable = "NTG"
non_net_layers_result.update()
non_net_layers.formation = "Not"
@@ -12,10 +12,10 @@ home_dir = expanduser("~")
properties = [
(rips.ResultType.STATIC_NATIVE, "INDEX_K", 0),
(rips.ResultType.STATIC_NATIVE, "PORO", 0),
(rips.ResultType.STATIC_NATIVE, "PERMX", 0),
(rips.ResultType.DYNAMIC_NATIVE, "PRESSURE", 0),
(rips.PropertyType.STATIC_NATIVE, "INDEX_K", 0),
(rips.PropertyType.STATIC_NATIVE, "PORO", 0),
(rips.PropertyType.STATIC_NATIVE, "PERMX", 0),
(rips.PropertyType.DYNAMIC_NATIVE, "PRESSURE", 0),
]
export_folder = tempfile.mkdtemp()
+5 -11
View File
@@ -30,15 +30,6 @@ from .surface import RegularSurface as RegularSurface # noqa: E402
from . import well_events as well_events # noqa: F401, E402
from . import category_mapping as category_mapping # noqa: F401, E402
# Enums for proto types not visible to the AppEnum auto-generator.
# PorosityModelType and PropertyDataType are auto-generated into resinsight_classes
# (already imported above) and intentionally not re-imported here to avoid a
# duplicate symbol.
from .enums import ( # noqa: E402
PropertyType as PropertyType,
NNCPropertyType as NNCPropertyType,
)
__all__: List[str] = []
for key in class_dict(): # noqa: F405
__all__.append(key)
@@ -47,10 +38,13 @@ for key in class_dict(): # noqa: F405
__all__.append("Grid")
__all__.append("Instance")
# PropertyType, PropertyDataType, and PorosityModelType are auto-generated
# StrEnum classes (driven by their caf::AppEnum<T> registrations) that the
# wildcard import above pulls in but class_dict() does not list.
for _enum_name in (
"PropertyType",
"PorosityModelType",
"PropertyDataType",
"NNCPropertyType",
"PropertyType",
):
if _enum_name not in __all__:
__all__.append(_enum_name)
+1 -3
View File
@@ -55,6 +55,7 @@ from .resinsight_classes import (
GeoMechCase as GeoMechCase,
PorosityModelType as PorosityModelType,
PropertyDataType as PropertyDataType,
PropertyType as PropertyType,
Reservoir as Reservoir,
WellBoreStabilityPlot as WellBoreStabilityPlot,
WbsParameters as WbsParameters,
@@ -66,9 +67,6 @@ from .project import Project as Project
from .pdmobject import add_method
from .view import View as View
from .simulation_well import SimulationWell
from .enums import (
PropertyType,
)
import rips.project # full name import due to circular dependency
-37
View File
@@ -1,37 +0,0 @@
"""Typed enums mirroring the proto enums used by the rips.case API.
These are hand-written counterparts to proto enums defined in ``Properties.proto``
and ``NNCProperties.proto``. They give users typed, autocompletable enum members
while remaining wire-compatible with the existing string-based API (``StrEnum``
members are strings).
Auto-generated ``StrEnum`` classes for ``caf::AppEnum`` scriptable fields and for
PdmObjectMethod enum return values live in ``rips.generated.generated_classes``
and are emitted by ``cafPdmPythonGenerator``. ``PorosityModelType`` and
``PropertyDataType`` are auto-generated and imported from ``resinsight_classes``;
the enums in this module cover the gRPC-only proto enums that the auto-generator
does not see.
"""
from enum import StrEnum
# Keep in sync with PropertyType in GrpcInterface/GrpcProtos/Properties.proto
class PropertyType(StrEnum):
DYNAMIC_NATIVE = "DYNAMIC_NATIVE"
STATIC_NATIVE = "STATIC_NATIVE"
SOURSIMRL = "SOURSIMRL"
GENERATED = "GENERATED"
INPUT_PROPERTY = "INPUT_PROPERTY"
FORMATION_NAMES = "FORMATION_NAMES"
FLOW_DIAGNOSTICS = "FLOW_DIAGNOSTICS"
INJECTION_FLOODING = "INJECTION_FLOODING"
REMOVED = "REMOVED"
UNDEFINED = "UNDEFINED"
# Keep in sync with NNCPropertyType in GrpcInterface/GrpcProtos/NNCProperties.proto
class NNCPropertyType(StrEnum):
NNC_DYNAMIC = "NNC_DYNAMIC"
NNC_STATIC = "NNC_STATIC"
NNC_GENERATED = "NNC_GENERATED"
@@ -26,10 +26,10 @@ def test_10k_well_log_extraction(rips_instance, initialize_test):
well_path = wells[0]
properties = [
(rips.ResultType.STATIC_NATIVE, "INDEX_K", 0),
(rips.ResultType.STATIC_NATIVE, "PORO", 0),
(rips.ResultType.STATIC_NATIVE, "PERMX", 0),
(rips.ResultType.DYNAMIC_NATIVE, "PRESSURE", 0),
(rips.PropertyType.STATIC_NATIVE, "INDEX_K", 0),
(rips.PropertyType.STATIC_NATIVE, "PORO", 0),
(rips.PropertyType.STATIC_NATIVE, "PERMX", 0),
(rips.PropertyType.DYNAMIC_NATIVE, "PRESSURE", 0),
]
well_log_plot_collection = rips_instance.project.descendants(