Skip to content

Comments

Typed objects block model#216

Open
denis-simo wants to merge 20 commits intoSeequentEvo:mainfrom
BenLewis-Seequent:typed-objects-block-model
Open

Typed objects block model#216
denis-simo wants to merge 20 commits intoSeequentEvo:mainfrom
BenLewis-Seequent:typed-objects-block-model

Conversation

@denis-simo
Copy link
Contributor

@denis-simo denis-simo commented Feb 19, 2026

This PR brings block models under the same typed object envolope as recently added objects.

Key parts of this pull requets:

  • BLock models are now an OPTIONAL dependency for objects. We want to have a single point of entry and block models on evo are not just references from the perspective of the user - they are actual block models that the user expects to interact with. If the user does not install block models, they can interact with block model metadata handling the current dependency in a similar way to pandas and numpy. Evo-sdk user will get the whole package, sneaky developer who only cares about very thin set of changes - can get evo-objects with cut down dependencies.

  • Block model dependencies were adjusted so that by default it can be used just to parse metadata. Interaction with data is now an optional dependency, this way for basic services that don't care about interacting with evo-data and that user evo-objects there is no extra dependency added.

  • Block models support dataframe conversions, pretty printing via widgets, report creation, and automated mapping/creation from object_from_reference/path.

  • I have shared typed definitions in Common, without going as far as adding shared functionality that relies on numpy to avoid new depenency on common.

  • Block model in objects acts as a wrapper for a fully featured typed objects in evo-block models that can exist by itself.

  • I have not added any converters to and from block model and 3D grid... but it is not that hard as interfaces are similar though not identical and because the objects are different enough - we do not want to have more shared interfaces at this stage other than common base blocks.

  • OCTREE and FULLY SUBBLOCKED modes support was left out for now (via typed Objects) - PR is large already

This is a meaninful update, so I have bumped up the versions. If we want to release this, I can write up the changelong now as well, though there is one more PR to come after this for Kriging compute.

Checklist

  • I have read the contributing guide and the code of conduct

…ion and more convenient interactions as part of notebooks by novice python users (geologists). Updated some of the method names to match pandas/tables syntax.
# Conflicts:
#	packages/evo-widgets/docs/examples/displaying-typed-objects.ipynb
#	packages/evo-widgets/src/evo/widgets/formatters.py
…jects-variogram

# Conflicts:
#	code-samples/geoscience-objects/README.md
#	code-samples/geoscience-objects/simplified-object-interactions/simplified-object-interactions.ipynb
#	packages/evo-objects/docs/examples/typed-objects.ipynb
#	packages/evo-objects/src/evo/objects/typed/__init__.py
#	packages/evo-objects/src/evo/objects/typed/base.py
#	packages/evo-widgets/README.md
#	packages/evo-widgets/docs/examples/displaying-typed-objects.ipynb
#	packages/evo-widgets/pyproject.toml
#	packages/evo-widgets/src/evo/widgets/__init__.py
#	packages/evo-widgets/src/evo/widgets/formatters.py
#	packages/evo-widgets/src/evo/widgets/urls.py
#	packages/evo-widgets/tests/test_formatters.py
#	packages/evo-widgets/tests/test_urls.py
#	uv.lock
…ped-objects-block-model

# Conflicts:
#	code-samples/geoscience-objects/running-kriging-compute/running-kriging-compute.ipynb
…jects-block-model

# Conflicts:
#	code-samples/geoscience-objects/running-kriging-compute/running-kriging-compute.ipynb
#	packages/evo-widgets/src/evo/widgets/__init__.py
#	packages/evo-widgets/src/evo/widgets/formatters.py
#	packages/evo-widgets/tests/test_formatters.py
@denis-simo denis-simo requested review from a team as code owners February 19, 2026 23:47
@denis-simo denis-simo requested a review from a team as a code owner February 22, 2026 20:08
Copy link

@RyanMillerSeequent RyanMillerSeequent left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have only looked at packages/evo-objects and everything LGTM there.

Comment on lines +53 to +62
def pyarrow_to_dataframe(table: "pa.Table") -> "pd.DataFrame":
"""Convert a PyArrow Table to a pandas DataFrame.

:param table: The PyArrow Table to convert.
:return: A pandas DataFrame.
:raises ImportError: If pyarrow or pandas is not installed.
"""
_check_pyarrow_available()
_check_pandas_available()
return table.to_pandas()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I prefer not to use string-literal annotations. In this case I think either including from __future__ import annotations, or conditionally defining the implementation (consistent with evo.objects.DownloadedObject.download_table(...), for example) would be acceptable.

Comment on lines +18 to +20
if TYPE_CHECKING:
import pandas as pd
import pyarrow as pa
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like pandas and pyarrow are always required by typed block models, so it doesn't make sense to defer annotation here

Comment on lines +33 to +50
def _check_pyarrow_available() -> None:
"""Check if pyarrow is available, raise ImportError with helpful message if not."""
try:
import pyarrow # noqa: F401
except ImportError as e:
raise ImportError(
"pyarrow is required for this operation. Install it with: pip install evo-blockmodels[pyarrow]"
) from e


def _check_pandas_available() -> None:
"""Check if pandas is available, raise ImportError with helpful message if not."""
try:
import pandas # noqa: F401
except ImportError as e:
raise ImportError(
"pandas is required for this operation. Install it with: pip install evo-blockmodels[pyarrow]"
) from e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, these checks seem redundant

) from e


def pyarrow_to_dataframe(table: "pa.Table") -> "pd.DataFrame":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I prefer not to use string literal annotations, seeing as from __future__ import annotations was added in python 3.7, and starting with python 3.14, annotations are lazily evaluated by default.

These types are re-exported from evo.common.typed for backward compatibility.
"""

from evo.common.typed import BoundingBox, Point3, Size3d, Size3i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that types have been refactored into the common module I think it would be worth updating the relevant imports wherever they are used. I don't see any reason to keep this module around


[project.optional-dependencies]
pyarrow = ["pyarrow>=19.0.0"]
pyarrow = ["pyarrow>=19.0.0", "pandas>=2.0.0"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should rename this optional dependency group to utils for consistency with evo-objects

ReportResult,
ReportSpecificationData,
)
from .types import BoundingBox, Point3, Size3d, Size3i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though these are now provided by evo.common.typed, I think it is still useful to export them from this namespace for convenience. Perhaps import them from evo.common.typed though

Comment on lines +35 to +43
if TYPE_CHECKING:
from ..client import BlockModelAPIClient
from ..endpoints.models import (
ReportResult as APIReportResult,
)
from ..endpoints.models import (
ReportSpecificationWithJobUrl,
ReportSpecificationWithLastRunInfo,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general I think it is cleaner to avoid if TYPE_CHECKING: guards unless they are absolutely necessary.

Suggested change
if TYPE_CHECKING:
from ..client import BlockModelAPIClient
from ..endpoints.models import (
ReportResult as APIReportResult,
)
from ..endpoints.models import (
ReportSpecificationWithJobUrl,
ReportSpecificationWithLastRunInfo,
)
from ..client import BlockModelAPIClient
from ..endpoints.models import ReportResult as APIReportResult
from ..endpoints.models import ReportSpecificationWithJobUrl, ReportSpecificationWithLastRunInfo

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the existing convention for using generated models is to import them as:

Suggested change
if TYPE_CHECKING:
from ..client import BlockModelAPIClient
from ..endpoints.models import (
ReportResult as APIReportResult,
)
from ..endpoints.models import (
ReportSpecificationWithJobUrl,
ReportSpecificationWithLastRunInfo,
)
from ..client import BlockModelAPIClient
from ..endpoints import models

Then reference them like so:

def _from_api_result(cls, result: models.ReportResult) -> ReportResult:

This eliminates the need for aliasing and makes it difficult to accidentally export generated models.

Comment on lines +65 to +70
def _require_blockmodels(operation: str = "This operation") -> None:
"""Raise ImportError if evo-blockmodels is not installed."""
if not _BLOCKMODELS_AVAILABLE:
raise ImportError(
f"{operation} requires the 'evo-blockmodels' package. Install it with: pip install evo-objects[blockmodels]"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would guard the method definitions where evo.blockmodels is used, rather than raise an exception.

Do consider whether you expect any of the types in this module to be used without evo-blockmodels installed.

Comment on lines +30 to +33
from .typed import BoundingBox as BoundingBox
from .typed import Point3 as Point3
from .typed import Size3d as Size3d
from .typed import Size3i as Size3i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these aliased?

Suggested change
from .typed import BoundingBox as BoundingBox
from .typed import Point3 as Point3
from .typed import Size3d as Size3d
from .typed import Size3i as Size3i
from .typed import BoundingBox, Point3, Size3d, Size3i

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants