07.04.2014 Views

Ocean Developer's Guide - Ocean - Schlumberger

Ocean Developer's Guide - Ocean - Schlumberger

Ocean Developer's Guide - Ocean - Schlumberger

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Getting Started with <strong>Ocean</strong><br />

For Geoscientists and Software Developers<br />

Published by <strong>Schlumberger</strong> Information Solutions,<br />

5599 San Felipe, Houston Texas 77056


Getting Started with <strong>Ocean</strong><br />

Copyright Notice<br />

Copyright © 2008 <strong>Schlumberger</strong>. All rights reserved.<br />

The information in this document is subject to change without notice. The<br />

software described in this document is furnished under a license agreement.<br />

This software may be used or copied only in accordance with the terms of<br />

such agreement. It is against the law to copy the software on any medium<br />

except as specifically allowed in the license agreement. No part of this<br />

document may be reproduced or transmitted in any form, or by any means,<br />

electronic or mechanical, including photocopying and recording, for any<br />

purpose without the express written permission of <strong>Schlumberger</strong>.<br />

Trademarks<br />

Petrel and <strong>Ocean</strong> are trademarks of <strong>Schlumberger</strong>.<br />

Microsoft® and Windows® are registered trademarks of Microsoft<br />

Corporation.<br />

ii


Table of Contents<br />

Chapter 1 Welcome to <strong>Ocean</strong> ...................................................................... 1<br />

Introduction .................................................................................................................. 1<br />

Access to Petrel Data Domain ................................................................................. 4<br />

<strong>Ocean</strong> UI Infrastructure ............................................................................................. 6<br />

The <strong>Ocean</strong> Module ..................................................................................................... 7<br />

IModule Interface .................................................................................................. 9<br />

Chapter 2 Your First Application ............................................................... 12<br />

Writing the Application ............................................................................................ 12<br />

Starting with Visual Studio ................................................................................. 13<br />

Inspect the Files ................................................................................................... 14<br />

Write Algorithm Code .......................................................................................... 18<br />

Running the Application .......................................................................................... 20<br />

Chapter 3 The Petrel Data Domain ............................................................ 24<br />

<strong>Ocean</strong> Exposes Petrel’s Data Model ..................................................................... 24<br />

Entities and Properties ........................................................................................ 24<br />

User View of the Petrel Data Model ................................................................. 26<br />

Data Access ............................................................................................................... 32<br />

Data Types Exposed ............................................................................................ 33<br />

Read Access .............................................................................................................. 38<br />

Browsing through collections ............................................................................ 38<br />

Seismic ................................................................................................................... 39<br />

Well and Geology ................................................................................................. 40<br />

Static Model .......................................................................................................... 42<br />

Simulation and Data Analysis ............................................................................ 43<br />

Data Updates ............................................................................................................. 44<br />

Transactions.......................................................................................................... 45<br />

Modifying Domain Objects ................................................................................. 45<br />

Domain Object Relationships ............................................................................. 46<br />

Domain Object Creation ........................................................................................... 48<br />

iii


Table of Contents<br />

Creating New Instances ..................................................................................... 48<br />

Creating New Collections ................................................................................... 49<br />

Deletions ..................................................................................................................... 52<br />

Data Access Example .............................................................................................. 52<br />

Online Manual for Data Domain ............................................................................. 55<br />

Opening the manual ............................................................................................. 55<br />

Using Intellisense ................................................................................................. 56<br />

Class Definitions ................................................................................................... 57<br />

Chapter 4 Extending the Petrel UI ............................................................. 60<br />

How to Add a New Menu Item ............................................................................... 60<br />

Edit IntegratePresentation method ................................................................... 61<br />

Creating UI Tools .................................................................................................. 61<br />

Define the Menu Item Properties ...................................................................... 62<br />

Add to ToolsManager .......................................................................................... 64<br />

Results ........................................................................................................................ 64<br />

Chapter 5 Extending the Data Domain ...................................................... 66<br />

Basic Custom Domain Object ................................................................................. 67<br />

Adding to Input and Models Trees ........................................................................ 68<br />

Adding to Native Petrel Domain Objects ......................................................... 69<br />

Adding an Object from a Context Menu ........................................................... 70<br />

Customizing Tree Presentation .......................................................................... 72<br />

Object Visualization .................................................................................................. 74<br />

3D Window Display .............................................................................................. 74<br />

Map Window Display .......................................................................................... 77<br />

Saving Custom Domain Objects ............................................................................. 81<br />

Serialization ........................................................................................................... 81<br />

iv


Chapter 1<br />

Welcome to <strong>Ocean</strong><br />

Introduction<br />

<strong>Ocean</strong> is an application development framework with the capability<br />

to work across data domains. It provides services, components,<br />

and a common graphical user interface that enables applications to<br />

better integrate. It also allows application developers to interact<br />

with other <strong>Ocean</strong> Model-Centric applications such as Petrel.<br />

<strong>Ocean</strong> Model-Centric applications are loaded dynamically as .NET<br />

assemblies. These assemblies, the building blocks of <strong>Ocean</strong>,<br />

contain Modules.<br />

The <strong>Ocean</strong> architecture has three levels: the Core, the Services and<br />

the product family. For Model-Centric applications, the product<br />

family is Petrel. <strong>Ocean</strong> modules are managed by the Core layer.<br />

They interact with all levels of the framework, as shown below:<br />

1


Welcome to <strong>Ocean</strong><br />

Application Module<br />

Deployed as plug-in or extension module<br />

<strong>Schlumberger</strong> or 3rd party<br />

Product Family<br />

<strong>Ocean</strong> Services<br />

<strong>Ocean</strong> Core<br />

Figure 1-1 - <strong>Ocean</strong> Architecture<br />

The <strong>Ocean</strong> Core plays the role of the basic infrastructure. It<br />

manages <strong>Ocean</strong> modules, registers services, both the services preloaded<br />

by the product family and that are added dynamically via the<br />

API. The <strong>Ocean</strong> Core manages the data sources provided by the<br />

product family or any external data source that could be defined by<br />

any module. It also performs event management and basic<br />

message logging.<br />

The <strong>Ocean</strong> Services are a set of application independent utilities.<br />

They are modules that benefit from being standardized across<br />

product families. An example is the Coordinate Service – a utility for<br />

converting between projection and geodetic co-ordinate systems.<br />

The <strong>Ocean</strong> Services layer only depends on the <strong>Ocean</strong> Core or on<br />

other <strong>Ocean</strong> Services.<br />

2


Welcome to <strong>Ocean</strong><br />

The product family is the host for <strong>Ocean</strong> applications, and is the<br />

environment the <strong>Ocean</strong> module needs to run in. The product family<br />

provides:<br />

• the domain objects and their data source<br />

• the graphical environment in which the applications will<br />

display their data<br />

• a common look and feel for all application user interface<br />

components<br />

Application modules connect to all software layers as well as to the<br />

.NET framework. Application modules can register their own<br />

services with the <strong>Ocean</strong> Core and benefit from services registered<br />

by other modules. All applications built on the <strong>Ocean</strong> framework are<br />

designed in a similar fashion but they rely on a product family to<br />

build and run.


Welcome to <strong>Ocean</strong><br />

Access to Petrel Data Domain<br />

The application can access data in the following domains provided<br />

by <strong>Ocean</strong>:<br />

Well (for Petrophysics and Geology applications)<br />

Seismic (for Geophysics)<br />

Shapes (for Structural modeling)<br />

PillarGrid (for Geomodeling)<br />

Simulation (for Reservoir Evaluation)<br />

4


Figure 1-2 Surface with fault model and seismic data<br />

Welcome to <strong>Ocean</strong>


Welcome to <strong>Ocean</strong><br />

<strong>Ocean</strong> UI Infrastructure<br />

6<br />

<strong>Ocean</strong> provides the capability to extend Petrel’s user interface<br />

functionality. The <strong>Ocean</strong> Application Programming Interface (API)<br />

supports:<br />

• Windows: adding custom windows.<br />

• Renderers:<br />

o Adding renderers for domain objects (native and<br />

custom) in different windows.<br />

• Interactions:<br />

o Adding custom window modes to define interactions<br />

in different windows.<br />

o Object picking in different windows for its<br />

manipulation.<br />

• Menus and toolbars:<br />

o Adding new menus to Petrel window or extending<br />

Petrel menus.<br />

o Adding new toolbars with custom tools<br />

o Extending Petrel toolbars with custom tools.<br />

• Petrel project explorer:


Welcome to <strong>Ocean</strong><br />

o Adding custom objects in Petrel tree in a particular<br />

hierarchy.<br />

o Adding Processes and workflows in Petrel process<br />

diagram and Workflow editor.<br />

The <strong>Ocean</strong> Module<br />

An <strong>Ocean</strong> module is an extension to the product family. Application<br />

programmers create modules that behave just like any standard<br />

part of Petrel. The modules are compiled into assemblies that are<br />

placed in the Petrel installation directory and registered with the<br />

application through the configuration file. Petrel uses the<br />

configuration file to load all registered modules during startup.


Welcome to <strong>Ocean</strong><br />

Figure 1-3 - <strong>Ocean</strong> Modules<br />

The <strong>Ocean</strong> module has a defined lifecycle with certain<br />

requirements and restrictions that allow the clean integration into<br />

the product family. Its lifecycle phases serve to license the module,<br />

initialize and integrate it into the product family, add presentation<br />

interfaces, and remove all these when the module is unloaded. The<br />

appearance and interaction is seamlessly allows the product family<br />

to treat the module as native code.<br />

An <strong>Ocean</strong> module uses the functionalities provided by the product<br />

family, the <strong>Ocean</strong> Services, the <strong>Ocean</strong> Core, and also the .NET<br />

architecture. It may also use 3rd party application assemblies and<br />

other modules.<br />

<strong>Ocean</strong> modules are built with Visual Studio 2005 for .NET with the<br />

help of the <strong>Ocean</strong> wizard. The wizard takes care of the interaction<br />

with the Core and some low-level access to functionality provided<br />

by the product family.<br />

8


Welcome to <strong>Ocean</strong><br />

IModule Interface<br />

An <strong>Ocean</strong> module implements the IModule interface. The product<br />

family is responsible for loading the module. IModule is defined in<br />

the Slb.<strong>Ocean</strong>.Core namespace.<br />

The IModule interface defines five methods of the module<br />

lifecycle phases and inherits from IDisposable. The phases and<br />

their methods are:<br />

• construction (default constructor)<br />

• initialization (Initialize)<br />

• integration (Integrate)<br />

• presentation integration (IntegratePresentation)<br />

• disintegration (Disintegrate)<br />

• disposal (Dispose)<br />

Default Constructor<br />

During the product startup the <strong>Ocean</strong> Core will load modules as<br />

defined in the configuration file for the product family. The <strong>Ocean</strong><br />

Core will call the default constructor to instantiate each module.<br />

The constructor could be used to acquire any licenses, initialize any<br />

private fields and acquire any resources the module needs.<br />

Initialization<br />

The second phase of the module lifecycle is the execution of the<br />

Initialize method. The main purpose of Initialize is<br />

registration of the services provided by the module with the <strong>Ocean</strong><br />

ServiceLocator class. When initialization is complete the


Welcome to <strong>Ocean</strong><br />

services registered are available for consumption by all <strong>Ocean</strong><br />

modules.<br />

Integrate<br />

The Integrate method is the first point at which a module may<br />

consume services registered with <strong>Ocean</strong>. This is because all<br />

modules have been through the initialize phase where their<br />

services are registered with <strong>Ocean</strong>. The <strong>Ocean</strong> Core<br />

ServiceLocator class is used to look up services.<br />

Integrate Presentation<br />

The IntegratePresentation method of IModule is the phase<br />

in the module lifecycle in which user interface components (menus,<br />

toolbars, context menus, windows etc.) are added to the product<br />

family.<br />

Disintegrate<br />

When the product family begins to shut down the IModule<br />

Disintegrate method is called on each module. Disintegrate<br />

has the responsibility of cleaning up any presentation elements<br />

installed by the module.<br />

Dispose<br />

Dispose comes from the IDisposable pattern and must be<br />

implemented as part of your IModule implementation. The purpose<br />

of Dispose is to free any unmanaged resources and free any<br />

licenses acquired by the module.<br />

10


Welcome to <strong>Ocean</strong><br />

Example<br />

The following is an example module.<br />

public class MyModule : IModule<br />

{<br />

// Perform initializations, acquire licenses if any<br />

public Module ()<br />

{<br />

...<br />

}<br />

// Initialize services provided by the module<br />

public Initialize ()<br />

{<br />

// Create the service<br />

object FooBar = new FooBarService();<br />

}<br />

// Add the service by its type<br />

CoreSystem.Services.AddService(typeof(FooBarService), FooBar);<br />

...<br />

// Integrate services provided by other modules<br />

public Integrate ()<br />

{<br />

// Find the coordinate system service<br />

ICoordSysService coordService;<br />

coordService = CoreSystem.GetService() as<br />

ICoordSysService;<br />

...<br />

}<br />

// Add to user interface of product family<br />

public IntegratePresentation ()<br />

{<br />

// Define the button to add<br />

ToolbarItem btn = new ToolbarItem(WellKnownUI.Menus.Insert,<br />

“MyButton”, ButtonClick);<br />

}<br />

// Add the button to the Insert menu of the Petrel menubar<br />

PetrelSystem.Tools.Add(btn);<br />

...<br />

// Remove components added to the user interface. Remove services<br />

public Disintegrate ()<br />

{<br />

// Remove the button added to the Insert menu<br />

PetrelSystem.Tools.Remove(btn);<br />

}<br />

// Remove our service<br />

CoreSystem.Services.RemoveService(typeof(FooBarService));<br />

...<br />

// Dispose of any unmanaged resources<br />

public Dispose ()<br />

{<br />

...<br />

}


Your First Application<br />

<strong>Ocean</strong> allows application developers to extend the Petrel<br />

functionality with new custom processes and workflows. In this<br />

chapter, we will walk through the procedure of creating a simple<br />

<strong>Ocean</strong> process.<br />

Writing the Application<br />

For our first application we will write an <strong>Ocean</strong> module that adds a<br />

process to Petrel. The process will print the names of all Seismic<br />

cubes in the current project.<br />

A Process in Petrel is an interactive operation performed on data.<br />

The Process is available from the Petrel Process diagram. The<br />

operation could create new data or update existing data. For<br />

example, the operation of using a set of points to create a well path<br />

would be referred to as a process. In this example the set of points<br />

is the input and the well path is the output.<br />

A Workstep is a single processing step. It differs from a Process in<br />

that it does not require manual interaction when executing. This is<br />

why it can take part in a workflow. Worksteps are available from<br />

12


Your First Application<br />

the Petrel Workflow editor; they also take input data and<br />

parameters, perform some processing, and produce output data.<br />

A Process can be created from a Workstep, so you can write code<br />

one time that is available in both the Process diagram and the<br />

Workflow editor.<br />

We will use the <strong>Ocean</strong> Wizard to create an <strong>Ocean</strong> process from a<br />

workstep for our first application.<br />

These are the steps to create your first application:<br />

1. Run the <strong>Ocean</strong> Wizard in Visual Studio to create the<br />

application module and process.<br />

2. Inspect the files created by the Wizard.<br />

3. Modify the code to add the processing logic.<br />

For details on the installation and use of the <strong>Ocean</strong> Wizard please<br />

refer to the <strong>Ocean</strong> Wizard Help manual.<br />

For details on processes and worksteps, please see the Workflow<br />

chapter in the <strong>Ocean</strong> Developers <strong>Guide</strong>.<br />

Starting with Visual Studio<br />

Start up Visual Studio.<br />

Create a new project using File-> New Project option. Select <strong>Ocean</strong><br />

Petrel Module template from <strong>Ocean</strong> under Visual C# project type.


Your First Application<br />

<strong>Ocean</strong><br />

Project<br />

Type<br />

<strong>Ocean</strong> Module<br />

Project Name<br />

Create an <strong>Ocean</strong> module with a process that has no arguments and<br />

an auto-generated UI.<br />

Provide the name “ListSeismic” for the project. It’s generally a good<br />

practice to use the name “Module” for the application module.<br />

Add a new process and add to the process a workstep with the<br />

name “ListSeismicCubes” with its own process wrapper. See the<br />

<strong>Ocean</strong> Wizard help for details. Do not specify any workstep<br />

argument.<br />

Inspect the Files<br />

The <strong>Ocean</strong> Wizard will create a solution named “ListSeismic” with<br />

a project named “ListSeismic” in the Visual Studio Solution<br />

Explorer. The project will contain source files for the Module class<br />

14


Your First Application<br />

that was created, and the ListSeismicCubes process that is added<br />

to the Process diagram.<br />

workstep<br />

source file<br />

module<br />

source file<br />

Figure 0-1: Example project source files in Solution Explorer<br />

The wizard configures the ListSeismic project to help debugging.<br />

The assembly is output to the Petrel installation directory. The<br />

project command line property is set up to start Petrel. So<br />

debugging is simply a matter of building and starting the debugger.<br />

The wizard writes the basic code needed for the ListSeismicCubes<br />

application. We will need to add code for our specialized algorithm<br />

in order to print the names of all Seismic Cubes in the current<br />

project.<br />

Module<br />

The Module class implements the IModule interface. Our new<br />

process is added to the Process manager in the Integrate<br />

method automatically by the Wizard.<br />

First the new ListSeismicCubes workstep is created.<br />

public void Integrate()<br />

{<br />

// Registrations:<br />

ListSeismicCubes sWorkstep = new ListSeismicCubes();


Your First Application<br />

Then it is added to the Workflow editor.<br />

PetrelSystem.WorkflowEditor.Add(sWorkstep);<br />

Finally, the process is created from the workstep using the<br />

WorkstepProcessWrapper convenience class. The new<br />

process is added to the Process diagram using the<br />

PetrelSystem.ProcessDiagram API.<br />

IProcessDiagram procDiag = PetrelSystem.ProcessDiagram;<br />

procDiag.Add(new WorkstepProcessWrapper(sWorkstep),"Plugins");<br />

}<br />

Putting the pieces together, the wizard generated the following<br />

Integrate method:<br />

public void Integrate()<br />

{<br />

// Registrations:<br />

ListSeismicCubes sWorkstep = new ListSeismicCubes();<br />

PetrelSystem.WorkflowEditor.Add(sWorkstep);<br />

IProcessDiagram procDiag = PetrelSystem.ProcessDiagram;<br />

procDiag.Add(new WorkstepProcessWrapper(sWorkstep),"Plugins");<br />

}<br />

ListSeismicCubes<br />

16<br />

The ListSeismicCubes class is the workstep class that holds<br />

the processing algorithm. We saw how a process was created<br />

from it in the module.<br />

ListSeismicCubes derives from Workstep, a generic class<br />

that includes the description of the arguments.<br />

public abstract class Workstep : Workstep<br />

where TArgPack : new ();<br />

{<br />

protected abstract void CopyArgumentPackageCore (<br />

TArgPack fromArgPackage,<br />

TArgPack toArgPackage);<br />

protected virtual TArgPack CreateArgumentPackageCore();<br />

protected abstract void InvokeSimpleCore(TArgPack argPackage);<br />

}<br />

The wizard implements nearly all of the class for you. Arguments<br />

copy can be done with the DescribedArgumentsHelper static<br />

class.


Your First Application<br />

public class ListSeismicCubes :<br />

Workstep<br />

{<br />

protected override void InvokeSimpleCore(<br />

ListSeismicCubes.Arguments argPackage)<br />

{<br />

...<br />

// TODO: finish the Invoke method implementation<br />

return;<br />

}<br />

}<br />

protected override void CopyArgumentPackageCore(<br />

ListSeismicCubes.Arguments fromArgPackage,<br />

ListSeismicCubes.Arguments toArgPackage)<br />

{<br />

DescribedArgumentsHelper.Copy(fromArgPackage, toArgPackage);<br />

}<br />

...<br />

The wizard also implements two interfaces, IPresentation (for<br />

its appearance in the Process diagram) and<br />

IDescriptionSource (to display information in the Process UI).<br />

public class ListSeismicCubes : IPresentation, IDescriptionSource<br />

{<br />

#region IPresentation Members<br />

}<br />

public event EventHandler PresentationChanged;<br />

public string Text { ... }<br />

public System.Drawing.Bitmap Image { ... }<br />

#endregion<br />

#region IDescriptionSource Members<br />

public IDescription Description { ... }<br />

#endregion<br />

...<br />

ListSeismicCubes also contains an Arguments class created<br />

by the wizard, which in our case is empty since our algorithm has<br />

no arguments.<br />

public class ListSeismicCubes:Workstep<br />

...<br />

{<br />

public class Arguments : DescribedArgumentsByReflection<br />

}<br />

{<br />

}<br />

...


Your First Application<br />

Write Algorithm Code<br />

The wizard wrote nearly all of our ListSeismicCubes class<br />

except for the custom algorithm code. We will add this code in the<br />

InvokeSimpleCore method, which will be called when the<br />

workstep or process is executed.<br />

To access the APIs from the Seismic namespace, add a reference<br />

in your project to the Slb.<strong>Ocean</strong>.Petrel.DomainObject.dll.<br />

This assembly is available in the Public folder of the Petrel<br />

installation directory.<br />

Next we need to add using statements for the required<br />

namespaces. Add the following statements at the top along with the<br />

other using statements.<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />

The work for the process is done in the InvokeSimpleCore<br />

method. We will add our code here:<br />

protected override void InvokeSimpleCore(<br />

ListSeismicCubes.Arguments argPackage)<br />

{<br />

if (argPackage == null)<br />

throw new ArgumentNullException("argPackage must ...");<br />

18<br />

}<br />

// TODO: finish the Invoke method implementation<br />

return;<br />

Our first step is to read the current project using the<br />

PetrelProject.PrimaryProject API.<br />

Project project = PetrelProject.PrimaryProject;<br />

The static class SeismicRoot is used to find the root of all domain<br />

objects in the seismic domain.<br />

SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />

It exposes a property SeismicProject which provides navigation<br />

to all seismic collections and interpretation collections in the<br />

project.


Your First Application<br />

SeismicProject seismicProj = seismicRoot.SeismicProject;<br />

Next we parse through all the seismic collections and print the<br />

names of all seismic cubes within them using the<br />

InfoOutputWindow method of the PetrelLogger class.<br />

We build a dynamic list of surveys (SeismicCollection) to<br />

avoid recursive calls while providing a breadth traversal of the<br />

survey tree.<br />

List listCol = new List(<br />

seismicProj.SeismicCollections);<br />

for (int idx = 0; idx < listCol.Count; idx++)<br />

{<br />

SeismicCollection current = listCol[idx];<br />

foreach (SeismicColl sCol in current.SeismicCollections)<br />

{<br />

// append to the list to replace tail recursion<br />

listCol.Add(sCol);<br />

}<br />

}<br />

foreach (SeismicCube cube in current.SeismicCubes)<br />

{<br />

// Print name of Seismic cube<br />

PetrelLogger.InfoOutputWindow(current.Name +<br />

" contains Seismic Cube " + cube.Name);<br />

}<br />

The PetrelLogger static convenience class provides static<br />

methods to show a textual display of information to users. The<br />

InfoOutputWindow is used to issue an information message<br />

intended for all end users to see, but which is not highly<br />

interrupting.<br />

The complete example follows:<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />

protected override void InvokeSimpleCore(<br />

ListSeismicCubes.Arguments x)<br />

{<br />

// Get current primary project<br />

Project proj = PetrelProject.PrimaryProject;<br />

// Get the root of all domain objects in the seismic domain<br />

SeismicRoot root = SeismicRoot.Get(proj);<br />

SeismicProject sProj = root.SeismicProject;<br />

// Find all seismic collections in the current project<br />

IEnumerable col = sProj.SeismicCollections;<br />

List listCol =<br />

new List(col);<br />

for(int idx = 0; idx < listCol.Count; idx++)


Your First Application<br />

}<br />

{<br />

SeismicCollection curr = listCol[idx];<br />

foreach (SeismicCollection sCol in curr.SeismicCollections)<br />

{<br />

listCol.Add(sCol);<br />

}<br />

// Find all Seismic cubes in the current collection<br />

foreach (SeismicCube cube in curr.SeismicCubes)<br />

{<br />

PetrelLogger.InfoOutputWindow(curr.Name +<br />

" contains Seismic Cube " + cube.Name);<br />

}<br />

}<br />

return;<br />

Running the Application<br />

After modifying the InvokeSimpleCore method, build the<br />

solution and start Petrel. When Petrel comes up, open the demo<br />

project deployed with the <strong>Ocean</strong> SDK. Then go to the Process<br />

diagram window and look for “ListSeismicCubes”.<br />

20


Your First Application<br />

Figure 0-2: ListSeismicCubes selected in Process diagram<br />

Double click on it to start the process and display the<br />

ListSeismicCubes dialog. The short and long descriptions are<br />

displayed in the top section of the dialog. There are no arguments<br />

for our process so the lower section is empty.


Your First Application<br />

Figure 0-3: ListSeismicCubes dialog<br />

Click Apply in the dialog to run the process; the ListSeismicCubes<br />

process will display the seismic cubes in the Petrel message log.<br />

22


Your First Application<br />

Figure 0-4: ListSeismicCubes output messages<br />

Exit Petrel. You have now written, built, and run your first <strong>Ocean</strong><br />

application.


The Petrel Data Domain<br />

<strong>Ocean</strong> Exposes Petrel’s Data<br />

Model<br />

<strong>Ocean</strong> accesses data stored in Petrel Projects. The API exposes<br />

the data objects contained in the current primary Project.<br />

Entities and Properties<br />

A Project built by Petrel contains data elements representing earth<br />

entities (a Well, a Surface, a Reservoir). These entities are<br />

characterized by Properties (such as Logs along a well bore, Height<br />

24


The Petrel Data Domain<br />

fields across a surface, Porosity distributions in a reservoir). Both<br />

types of data are exposed in <strong>Ocean</strong> by classes, called Domain<br />

Objects.<br />

The entity instance carries the geometry of the object it represents.<br />

The property object only carries property values.<br />

Property Versions<br />

Each Property object in <strong>Ocean</strong> has a Property Version attached to<br />

it, so that an application working with that Property has a way to:<br />

1 Distinguish between two properties with the same<br />

characteristics (versioning)<br />

2 Knows how to display the property values by applying the<br />

proper template (unit, scale and color table)<br />

Every Property type object in the Petrel Project has a Property<br />

Version member. When creating a Property with the API, a Property<br />

Version also has to be supplied, by extracting it from an existing<br />

object or by creating it with<br />

PetrelSystem.PropertyVersionService.<br />

IUnitMeasurement<br />

PropertyVersion<br />

ITemplate<br />

Figure 3-1 - Property Version, Unit Measurement and Template<br />

Classes<br />

To find or create a Property Version, we can refer to a Template<br />

which is known to Petrel. A list of such templates is available from<br />

the PetrelUnitSystem static class.


The Petrel Data Domain<br />

public void CreateProperty(...)<br />

{<br />

IPropertyVersionService pvs;<br />

pvs = PetrelSystem.PropertyVersionService;<br />

ITemplate templ = PetrelUnitSystem.TemplateGroupVolume.HCPVGas;<br />

IPropertyVersionContainer pvc =<br />

PetrelSystem.GetGlobalPropertyVersionContainer();<br />

PropertyVersion pv = pvs.FindOrCreate(pvc, templ);<br />

// Create Property instance with Property Version pv<br />

...<br />

return;<br />

}<br />

For more details on Property Versions (and Log Property Versions)<br />

refer to the <strong>Ocean</strong> Developer’s <strong>Guide</strong>.<br />

User View of the Petrel Data Model<br />

The Petrel Project organizes the user view of the Project in different<br />

trees:<br />

1 Input<br />

2 Models<br />

3 Results<br />

4 Cases<br />

All the trees are accessible via the API. Data is organized in<br />

pseudo-folders, seen in the API as object Collections.<br />

26


The Petrel Data Domain<br />

Input Data<br />

Input data gathers all data that is needed to build a static model.<br />

This includes all data acquired and processed (well logs, geological<br />

markers, seismic data sets, seismic attributes) and all data that is<br />

interpreted from the field measurements (petrophysical properties,<br />

seismic time interpretations, velocity functions).<br />

Most domain objects visible in the Input data tree are accessible in<br />

<strong>Ocean</strong> from the following specialized collections:<br />

1 BoreholeCollection – This collection class contains<br />

Boreholes, Logs and Completions, as well as nested<br />

BoreholeCollections. See the Well data model for details.


The Petrel Data Domain<br />

2 MarkerCollection – This is the Geology data model.<br />

These collections contain Markers, Horizons, Zones and<br />

Fluid Contacts.<br />

3 SeismicCollection – for 3D and 2D Seismic.<br />

4 InterpretationCollection – for all Seismic<br />

interpretation.<br />

5 Collection – Other folders are accessible as generic<br />

collections. From these collections, the API can access<br />

point sets, polyline sets and surfaces.<br />

Other objects in the Input data tree are inaccessible.<br />

28


The Petrel Data Domain<br />

Models<br />

The Models tree includes all elements of the Pillar grid static model<br />

and the property objects that represent Property distributions<br />

throughout the Pillar grid cell array.<br />

Domain objects visible in the Model data tree are accessible from<br />

the following collections:<br />

1 ModelCollection. This top level container in the Model<br />

tree enumerates all the Models (pillar grids) in the tree.<br />

Model collections are not nested.


The Petrel Data Domain<br />

2 Grid.PropertyCollection. Specific to each Model, the<br />

collection of all Property and FaultProperty instances in that<br />

Model.<br />

3 Fault.Properties. This lists all Fault Properties, per<br />

fault, in a pillar grid Model.<br />

Other parts of the Model tree (filters under pillar grids and Velocity<br />

models) are inaccessible.<br />

Results<br />

30<br />

Results are simulation results imported back in Petrel to compare<br />

the static models with its possible evolution through time. They are<br />

cross referenced by ResultProperty (the type of property the<br />

result lists versus time) and ResultCategory (earth model entity<br />

filter such as the Well for which that result data series is relevant).<br />

Results filters are organized in a hierarchy:


The Petrel Data Domain<br />

1 AnalysisRoot.ResultCategories lists all categories.<br />

Categories are nested. Omitting a category filter will retrieve<br />

cumulated results at the root category level.<br />

2 AnalysisRoot.ResultProperties lists all properties.<br />

ResultCategory and ResultProperty objects are required to<br />

retrieve time series results.<br />

Cases<br />

The Cases tree expose simulation cases based on different<br />

hypotheses but also represent various interpretation versions<br />

driving static model property variations. Cases contain<br />

CaseAnalysis objects (one level nesting).<br />

Simulation cases can be listed at the root level. Like other Analysis<br />

cases, Simulation cases are not nested:<br />

1 SimulationRoot.Simulations lists all cases which<br />

carry simulation data.<br />

Simulation results are referenced with the same result properties<br />

and result categories as general analysis results.


The Petrel Data Domain<br />

Data Access<br />

The <strong>Ocean</strong> API offers the following data access types:<br />

1 Read<br />

2 Update<br />

3 Create<br />

4 Delete<br />

The API reads existing data, browsing through data folders,<br />

enumerating collection contents and following the parts-parts-of<br />

relationships.<br />

The API updates existing objects, modifying entity characteristics<br />

(such as its geometry) and modifying Property values (like<br />

correcting log values in chosen points).<br />

The API creates new instances of Petrel object classes,<br />

augmenting the Project with new data the same way Petrel<br />

applications do.<br />

Last the API deletes objects that are no longer needed in the<br />

Project.<br />

32


The Petrel Data Domain<br />

Data Types Exposed<br />

The following tables show the API Read, Update, Create and Delete<br />

functionalities implemented in different parts of the Data Model.<br />

Details are given in the compiled html manual part of the <strong>Ocean</strong><br />

SDK release, <strong>Ocean</strong>.chm in the Documentation subdirectory.<br />

Seismic<br />

<strong>Ocean</strong> classes for Seismic are found in namespace<br />

Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic<br />

Object type Read Update Create Delete<br />

3D Cube<br />

2D line<br />

Horizon<br />

Interpretation<br />

Fault Interpretation<br />

2D line creation is restricted (to cloning a 2D survey). Virtual cubes<br />

are accessed via class IAttribute. See developers manual for<br />

details.<br />

Well<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />

Object type Read Update Create Delete


The Petrel Data Domain<br />

Borehole/Trajectory<br />

Log<br />

Checkshot<br />

Completion<br />

Different types of Logs are available, property valued well logs,<br />

facies well logs, point logs and multitrace well logs. We cover<br />

property valued logs in this guide, you can look for more log types<br />

and greater details in the developers manual.<br />

Geology<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />

Object type Read Update Create Delete<br />

Marker<br />

Horizon<br />

Zone<br />

Fault<br />

Interface<br />

Stratigraphy columns may be created across a number of wells.<br />

Fault and Interface type Surfaces are just named references.<br />

34


The Petrel Data Domain<br />

Shapes<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Shapes<br />

Object type Read Update Create Delete<br />

Regular Height Field<br />

Surface<br />

PointSet<br />

Property<br />

PolylineSet<br />

Properties can be added to Point Sets and Surfaces, not to Polyline<br />

Sets. Surfaces can be irregular or gridded (regular height field).<br />

Both can be assigned property values.<br />

Pillar Grid<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.PillarGrid<br />

Object type Read Update Create Delete<br />

Pillar Grid (Geometry)<br />

Property<br />

Horizon<br />

Fault, Zone, Segment<br />

Fault Property


The Petrel Data Domain<br />

In <strong>Ocean</strong> 2008 the geometry of the pillar grid is immutable, except<br />

for nudging a Horizon along a pillar. The structural elements of the<br />

grid (segments, faults, horizons and zones) cannot be changed nor<br />

removed. Properties and fault properties are fully accessible.<br />

Pillar grid property retains some knowledge of statistical treatment<br />

done at creation time, like which cells have been upscaled and<br />

statistical distributions of facies values. See the developers manual<br />

for details.<br />

Case analysis<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Analysis<br />

Object type Read Update Create Delete<br />

Case / Case Run<br />

Result Property<br />

Result Category<br />

Result vs Time<br />

A data analysis case has a number of case runs, each providing<br />

time series (results vs time) for combination of result categories<br />

(fields, wells) and result properties (oil production, water flow rate,<br />

etc.).<br />

36


The Petrel Data Domain<br />

Results<br />

Namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Simulation<br />

Object type Read Update Create Delete<br />

Simulation<br />

Streamline<br />

Streamline Property<br />

Streamlines can be added to various stream line sets in a<br />

simulation case. Property values can also be read and appended<br />

and new property types added.<br />

Simulation cases inherit from general case analyses and are the<br />

source of time series which are identical to data analysis results.


The Petrel Data Domain<br />

Read Access<br />

38<br />

Browsing through collections<br />

There is no query service implemented on the Petrel Project as it is<br />

not implemented in a relational database.<br />

Instead data browsing is done by following the non-taxonomic<br />

hierarchy.<br />

At the top level we have specific collections:<br />

1 SeismicCollection, InterpretationCollection<br />

2 BoreholeCollection<br />

3 MarkerCollection<br />

4 ModelCollection, PropertyCollection<br />

5 AnalysisRoot.ResultCategories<br />

6 AnalysisRoot.ResultProperties<br />

7 Cases, Simulations<br />

Collections are nested. It is possible to browse through the whole<br />

data domain of a project by accessing the root collection of each<br />

type.<br />

From each collection, the API can access the entities and the<br />

properties that are relevant to that collection type.


The Petrel Data Domain<br />

Seismic<br />

Seismic data is rooted in the Project’s SeismicProject instance,<br />

which is a singleton, once it has been created. It is accessed, if it<br />

exists, from the SeismicRoot of the project. It only exists if there<br />

are seismic data in the project.<br />

Below the SeismicProject, we have two separate hierarchies in<br />

the Seismic domain, one for seismic data, with nested<br />

SeismicCollection objects, the other for interpretation picks<br />

with a nested hierarchy of InterpretationCollection<br />

instances.<br />

Project proj = PetrelProject.PrimaryProject;<br />

SeismicRoot sr = SeismicRoot.Get(proj);<br />

SeismicProject sp = sr.GetOrCreateSeismicProject();<br />

// navigate the Seismic data hierarchy<br />

foreach (SeismicCollection scol in sp.SeismicCollections)<br />

{<br />

...<br />

}<br />

// navigate the Seismic interpretation hierarchy<br />

foreach(InterpretationCollection icol in<br />

sp.InterpretationCollections)<br />

{<br />

...<br />

}


The Petrel Data Domain<br />

Seismic data in a SeismicCollection include either seismic<br />

cubes or seismic 2D surveys (collections of lines).<br />

Interpretation data include HorizonInterpretation or<br />

FaultInterpretation instances each including 3D and 2D time<br />

picks that can be accessed separately.<br />

Well and Geology<br />

40<br />

Wells belong to a nested hierarchy of BoreholeCollection<br />

objects that contain boreholes, trajectories and logs.<br />

Markers are stored under stratigraphic columns or horizon, zone<br />

and marker collections. At the top level, we find a list of such


The Petrel Data Domain<br />

collections, implemented in the API by the MarkerCollection<br />

class.<br />

All well and geology objects are accessible at the root of the<br />

project from the WellRoot class. WellRoot is unique for the<br />

Project and retrieved from a static method in the WellRoot class.<br />

Project proj = PetrelProject.PrimaryProject;<br />

WellRoot wr = WellRoot.Get(proj);<br />

// Get top level Borehole Collection<br />

BoreholeCollection bhcol = wr.BoreholeCollections;<br />

...<br />

Once the WellRoot instance is retrieved, it gives access to<br />

borehole and marker collections.<br />

public sealed class WellRoot : ...<br />

{<br />

public BoreholeCollection BoreholeCollection { get; }<br />

...<br />

public int WellLogVersionCount { get; }<br />

public IEnumerable WellLogVersions { get; }<br />

...<br />

public int MarkerCollectionCount { get; }<br />

public IEnumerable MarkerCollections { get; }<br />

}<br />

Boreholes are found in nested collections, rooted at the top level<br />

collection retrieved from WellRoot.<br />

Marker collections are not nested. At the top level, list of<br />

collections are found, each corresponding to a different<br />

stratigraphic classification.


The Petrel Data Domain<br />

Static Model<br />

Pillar grids are in a hierarchy that contains the property hierarchies.<br />

The hierarchy retrieval starts from the PillarGridRoot object.<br />

PillarGridRoot grid = PillarGridRoot.Get(proj);<br />

IEnumerable mcolls;<br />

mcolls = ModelCollection.GetRootModelCollections(proj);<br />

foreach (ModelCollection mcol in mcolls)<br />

{<br />

foreach (Grid g in gr.GetGrids(mcol)<br />

{<br />

...<br />

}<br />

}<br />

Each Grid object interfaces a full static model with intersecting<br />

horizons and pillar-based faults.<br />

Grid g = ...<br />

foreach (Fault f in g.Faults)<br />

{<br />

...<br />

}<br />

foreach (Horizon hor in g.Horizons)<br />

{<br />

...<br />

42<br />

}<br />

The model is populated by property values, accessed in a nested<br />

hierarchy of PropertyCollection folders.<br />

Grid g = ...


The Petrel Data Domain<br />

PropertyCollection pcol = g.PropertyCollection;<br />

foreach (Property prop in pcol.Properties)<br />

{<br />

PropertyVersion pv = prop.PropertyVersion;<br />

// recurse on subcollections<br />

foreach (PropertyCollection subcol in pcol.PropertyCollections)<br />

{<br />

...<br />

}<br />

}<br />

Simulation and Data Analysis<br />

Case analyses, result properties and result hierarchies belong to<br />

separate hierarchies. The time series are defined by combinations<br />

of these.<br />

Simulation case runs are retrieved from SimulationRoot. These<br />

simulation cases runs give access to time series and streamlines.<br />

For time series, the result property and category has to be supplied.<br />

SimulationRoot root = ...<br />

foreach (Simulation sim in root.Simulations)<br />

{<br />

StreamlineSet sls = sim.StreamlineSet;<br />

if (sls != StreamlineSet.NullObject)<br />

{<br />

foreach (StreamlineSubset sub in sls.StreamlineSubsets)<br />

{<br />

foreach (Streamline sl in sub.Streamlines)


The Petrel Data Domain<br />

}<br />

}<br />

}<br />

{<br />

}<br />

...<br />

Result properties and categories are retrieved from<br />

AnalysisRoot. With a combination of property and category a<br />

simulation case run will return the relevant time series.<br />

AnalysisRoot aroot = ...<br />

List cats;<br />

cats = new List(aroot.ResultCategories);<br />

List props;<br />

props = new List(aroot.ResultProperties);<br />

int pi = ...<br />

int ci = ...<br />

foreach (TimeSeries ts in sim.GetTimeSeries(props[pi], cats[ci]))<br />

{<br />

...<br />

}<br />

Data Updates<br />

Data update is done by modifying the object that is seen through<br />

the API. Updating an entity or a property object alters the Project.<br />

After the project has been updated, it is the user who decides<br />

whether to save the Project changes or not. But from the API<br />

standpoint, the Project has been modified as soon as an object has<br />

been edited.<br />

Any such update has to be done inside a transaction and any<br />

modified object has to be locked beforehand inside that<br />

transaction.<br />

44


The Petrel Data Domain<br />

Transactions<br />

A transaction represents a group of edits on some data (domain<br />

objects). Transactions are required for operations on native Petrel<br />

domain objects that will change the data: create, update, and<br />

delete.<br />

Transactions use the Slb.<strong>Ocean</strong>.Core.ITransaction<br />

interface:<br />

public interface ITransaction : IDisposable<br />

{<br />

void Commit();<br />

void Lock(params object[ ] objects);<br />

void LockCollection(IEnumerable objectCollection);<br />

...<br />

}<br />

To use a transaction:<br />

1. Create a transaction<br />

2. Lock domain objects that are going to be changed<br />

3. Modify the domain objects<br />

4. Commit the transaction<br />

5. Dispose of the created transaction<br />

In Petrel, transactions are used to batch event notifications. Petrel<br />

does not use a data base, so changes to domain objects made<br />

within a transaction happen immediately.<br />

Modifying Domain Objects<br />

Domain objects can be modified in the transaction after having<br />

been locked. The on-line documentation will indicate which object<br />

property can be modified.


The Petrel Data Domain<br />

The nature of the class members is also explicitly shown in the<br />

class definitions that are accessible from the Visual Studio<br />

environment.<br />

namespace Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well<br />

{<br />

public sealed class WellLog : ...<br />

{<br />

public Borehole Borehole { get; }<br />

public string Comments { get; set; }<br />

...<br />

public int SampleCount { get; }<br />

public IEnumerable Samples { get; set; }<br />

...<br />

}<br />

}<br />

The array of log values, Samples, is read-write but SampleCount<br />

is read-only (dynamically evaluated from the array size).<br />

Domain Object Relationships<br />

46<br />

Object to object relationships can also be accessed via the API. For<br />

these relationships to be modifiable, the corresponding class<br />

property must allow write access, which is, as a general rule, not<br />

the case as parts-of relationships impose constraints between<br />

parent and child. For instance a pillar grid Property cannot be<br />

separated from the Grid object that defines its geometry.<br />

Even when the user can modify the containment hierarchy<br />

interactively, the API often makes the corresponding relationship<br />

immutable.<br />

However there are cases where it makes sense to allow write<br />

access to parts-of relations. This is the case when object creation<br />

does not require such a relationship to be established. For instance,<br />

it is possible to update the reference from a Marker to a given<br />

Surface (Fault or Horizon).<br />

In the example below we clear the stratigraphic references of a set<br />

of markers in a given borehole.


The Petrel Data Domain<br />

MarkerCollection mcol = ...<br />

Borehole bh = ...<br />

Horizon hor = Horizon.NullObject;<br />

foreach (Marker m in mcol.GetMarkers(bh))<br />

{<br />

m.Surface = hor;<br />

}<br />

Please refer to the on-line help to check whether the relationship is<br />

read-only.


The Petrel Data Domain<br />

Domain Object Creation<br />

Creating New Instances<br />

48<br />

Most objects accessible via the API can be instantiated.<br />

However, the API never allows class instantiation in the classical<br />

manner. API classes are sealed, and some objects are even


The Petrel Data Domain<br />

exposed simply as interfaces. To create a new class instance, the<br />

API proposes specialized Create methods.<br />

Using Transactions<br />

All domain object creations have to be done inside transactions,<br />

just like object updates. But an object creation modifies the<br />

container that this new object will be part of. It modifies its parent<br />

entity (in the non-taxonomic hierarchy) or the collection of similar<br />

entities that will harbor it. That parent or collection has to be locked<br />

in the transaction.<br />

The newly created object can be modified until the transaction<br />

commits. It is locked by default, only its parent had to be locked<br />

explicitly in the transaction.<br />

Locating the Correct Create Method<br />

The Create method is always located in the class of the containing<br />

object. Borehole contains WellLog collections, that is where the<br />

Create method for logs can be found.<br />

using (ITransaction t = DataManager.NewTransaction())<br />

{<br />

PropertyVersion pv = ...<br />

Borehole bh = ...<br />

t.Lock(bh);<br />

WellLog wl = bh.Logs.CreateWellLog(pv);<br />

...<br />

}<br />

Creating New Collections<br />

When it does not make sense to create an entity in a predefined<br />

collection, one has to create a new collection. Collections are often<br />

nested, so adding a collection is adding a branch to a tree. Classes<br />

that model nested collections allow creation of sub-collections. For


The Petrel Data Domain<br />

example a PropertyCollection can contain sub-collections, and<br />

carries a Create method for branching out at that point.<br />

ITransaction t = ...<br />

PropertyCollection pcol = ...<br />

t.Lock(pcol);<br />

string colName = "New Sub-Collection";<br />

PropertyCollection subcol = pcol.CreatePropertyCollection(colName);<br />

New top-level collections<br />

For top-level objects (like collections) the Create methods are in<br />

static classes. For example, the top level collection for Seismic data<br />

is created in the SeismicProject instance.<br />

There are few collections that can appear at the top level of Petrel<br />

data trees. Nested collections are rooted under a top level<br />

collection that is only created once. Un-nested collections, that can<br />

be added at the root of Input or Models data trees are:<br />

1 MarkerCollection<br />

2 ModelCollection<br />

3 Collection<br />

These collections are created directly from the Petrel primary<br />

Project or the WellRoot object.<br />

ITransaction t = ...<br />

Project proj = PetrelProject.PrimaryProject;<br />

WellRoot root = WellRoot.Get(proj);<br />

t.Lock(proj);<br />

proj.CreateModelCollection("New Pillar Model");<br />

t.Lock(root);<br />

root.CreateMarkerCollection("New Stratigraphic Model");<br />

Nested Collections<br />

Collections that are always grouped under a root collection are<br />

created using a specific container, WellRoot for boreholes and<br />

50


The Petrel Data Domain<br />

SeismicProject for seismic data and interpretation. These are<br />

used for nested collections.<br />

1 SeismicCollection<br />

2 InterpretationCollection<br />

3 BoreholeCollection<br />

There is no specific object at the root of all borehole collections,<br />

instead we have a generic BoreholeCollection called “Wells”<br />

that can only be created once.<br />

ITransaction t = ...<br />

Project proj = PetrelProject.PrimaryProject;<br />

SeismicRoot root = SeismicRoot.Get(proj);<br />

SeismicProject sproj = root.SeismicProject;<br />

WellRoot wroot = WellRoot.Get(proj);<br />

t.Lock(sproj);<br />

string colName = "New Collection";<br />

SeismicCollection scol = sproj.CreateSeismicCollection(colName);<br />

String iName = “New Interpretation”);<br />

InterpretationCollection icol;<br />

icol = sproj.CreateInterpretationCollection(iName);<br />

...<br />

t.Lock(wroot);<br />

BoreholeCollection bcol = wroot. GetOrCreateBoreholeCollection();<br />

...<br />

We can also create collections that are placed under an object<br />

container, like under the PillarGrid.<br />

1 PropertyCollection<br />

ITransaction t = ...<br />

Grid g = ...<br />

t.Lock(g);<br />

PropertyCollection root = g.PropertyCollection;<br />

String colName = “New Properties”;<br />

Property>Collection sub = root.CreatePropertyCollection(colName);<br />

...


The Petrel Data Domain<br />

Deletions<br />

Deletion of an existing instance is done by calling the Delete<br />

method on the object itself.<br />

Deletion is always done inside a Transaction and the object to be<br />

deleted has to be locked first.<br />

Data Access Example<br />

In the following example we will browse through wells in the<br />

project, retrieve a porosity log, browse through static models and<br />

create a new Property for the latest modified model in the<br />

project.<br />

The created property will be filled with values extracted from the<br />

well log in cells where actual log measurements exist.<br />

52


The Petrel Data Domain<br />

Browsing through well logs to find a Porosity measurement<br />

Project proj = PetrelProject.PrimaryProject;<br />

WellRoot root = WellRoot.Get(proj);<br />

BoreholeCollection wells = root.BoreholeCollection;<br />

WellLog poro = WellLog.NullObject;<br />

ITemplate temp =<br />

PetrelUnitSystem.TemplateGroupPetrophysical.Porosity;<br />

IUnitMeasurement um = temp.UnitMeasurement;<br />

foreach (BoreholeCollection bhc in wells.BoreholeCollections)<br />

{<br />

foreach (Borehole bh in bhc)<br />

{<br />

foreach (WellLog l in bh.Logs.WellLogs)<br />

{<br />

if (l.WellLogVersion.UnitMeasurement.Equals(um))<br />

{<br />

poro = l;<br />

break;<br />

}<br />

}<br />

if (!poro.IsGood) break;<br />

}<br />

if (!poro.IsGood) break;<br />

}<br />

if (poro == WellLog.NullObject) return;<br />

PetrelLogger.InfoOutputWindow("Found log " + poro.Name);<br />

Retrieving the latest model<br />

Grid latest = Grid.NullObject;<br />

DateTime last = new DateTime(1980, 1, 1);<br />

PillarGridRoot proot = PillarGridRoot.Get(proj);


The Petrel Data Domain<br />

foreach (Grid g in proot.Grids)<br />

{<br />

if (g.LastModified.Time.CompareTo(last) >= 0)<br />

{<br />

latest = g;<br />

last = latest.LastModified.Time;<br />

}<br />

}<br />

if (latest == Grid.NullObject) return;<br />

PetrelLogger.InfoOutputWindow("Found grid " + latest.Name);<br />

Creating a new property collection in the model<br />

ITransaction t = DataManager.NewTransaction();<br />

PropertyCollection pcol = latest.PropertyCollection;<br />

t.Lock(pcol);<br />

string pcolName = “New Collection”;<br />

PropertyCollection newcol =<br />

pcol.CreatePropertyCollection(pcolName);<br />

Creating the pillar grid property<br />

IPropertyVersionService pvs = PetrelSystem.PropertyVersionService;<br />

PropertyVersion gpv = pvs.FindOrCreate(latest, um);<br />

t.Lock(newcol);<br />

Property prop = newcol.CreateProperty(gpv);<br />

Filling values<br />

IPillarGridIntersectionService ipgs;<br />

ipgs = CoreSystem.GetService();<br />

IPolyline3 traj = poro.Borehole.Trajectory.Polyline;<br />

IEnumerable cells;<br />

cells = ipgs.GetPillarGridPolylineIntersections(latest, traj);<br />

foreach (SegmentCellIntersection cell in cells)<br />

{<br />

Index3 ijk = cell.EnteringCell;<br />

Point3 xyz = cell.IntersectionPoint;<br />

double index = poro.Borehole.Transform(Domain.ELEVATION_DEPTH,<br />

xyz.Z, Domain.INDEX);<br />

WellLogSample[] sam = new WellLogSample[](poro.Samples);<br />

latest[ijk] = sam[(int)depth];<br />

}<br />

t.Commit();<br />

t.Dispose();<br />

54


The Petrel Data Domain<br />

Online Manual for Data Domain<br />

<strong>Ocean</strong> comes with an online manual exposing all the classes,<br />

methods and class members implemented in the API.<br />

Opening the manual<br />

In Visual Studio 2005, the Help menu includes <strong>Ocean</strong> help. Just<br />

select Contents.


The Petrel Data Domain<br />

Using Intellisense<br />

In Visual Studio, the <strong>Ocean</strong> library references (files with “.dll”<br />

extensions) let the environment select class members and methods<br />

from a list dynamically generated from the types of the variables in<br />

the code.<br />

56


The Petrel Data Domain<br />

This helps in selecting the correct class syntax without having to<br />

explicitly search for information in the on-line manual.<br />

Class Definitions<br />

It is possible, while editing code, to go directly to the class<br />

definition of the current variable declaration. Simply right-click on<br />

the class name and Visual Studio opens the class reference.


The Petrel Data Domain<br />

Other class definitions can be then explored in cascade.<br />

58


The Petrel Data Domain


Chapter 2<br />

Extending the Petrel UI<br />

<strong>Ocean</strong> allows application developers to customize and extend the<br />

Petrel application UI. In this chapter, we will explore a very simple<br />

customization: adding your own menu item to an existing Petrel<br />

menu.<br />

Please see the <strong>Ocean</strong> Developers <strong>Guide</strong> for details on using <strong>Ocean</strong><br />

to extend the Petrel UI in this and other ways.<br />

How to Add a New Menu Item<br />

These are the steps to create a new menu item:<br />

1. Edit your module’s IntegratePresentation method.<br />

2. Use the <strong>Ocean</strong> convenience class ToolbarItem to create<br />

the menu item.<br />

3. Define the menu item properties.<br />

4. Add the menu item to the Petrel UI using the <strong>Ocean</strong> class<br />

ToolsManager.<br />

60


Adding a New Menu Item<br />

Edit IntegratePresentation method<br />

Add your new menu item in the module’s<br />

IntegratePresentation method.<br />

public void IntegratePresentation ()<br />

{<br />

// add your new menu item here<br />

}<br />

Creating UI Tools<br />

A tool is an item that appears on menus and toolbars. When a tool<br />

appears on a menu as a menu item, it is drawn as a list entry with<br />

text and an optional image.<br />

tool in menu, no image<br />

tool in toolbar<br />

Figure 3-1 - Tools in Petrel UI<br />

<strong>Ocean</strong> provides the ToolbarItem convenience class to create<br />

tools such as a menu item.<br />

public class ToolbarItem : IToolbarItem, ...<br />

{<br />

public ToolbarItem();<br />

Create an instance of the ToolbarItem convenience class:<br />

ToolbarItem myListSeismic = new ToolbarItem();


Adding a New Menu Item<br />

Define the Menu Item Properties<br />

You control the basic properties of the new menu item. Text and UI<br />

location must be specified.<br />

public class ToolbarItem : IToolbarItem, ...<br />

{<br />

public string ParentKey { set; get; }<br />

public string Text { set; get; }<br />

You can also optionally provide an image and an event handler for a<br />

menu click.<br />

}<br />

public System.Drawing.Bitmap Image { set; get; }<br />

public event System.EventHandler Click;<br />

...<br />

Text<br />

Supply the text to be shown in the menu item:<br />

myListSeismic.Text = "List Seismic Cubes";<br />

UI Location<br />

To specify the UI location of the new menu item, you need to give<br />

the parent key. In this case, the parent is the existing Petrel menu<br />

that contains the new menu item.<br />

Figure 3-2 – Standard Petrel Menus<br />

62<br />

The <strong>Ocean</strong> WellKnownUI.Menus static convenience class has a<br />

public static string field for each existing Petrel menu.<br />

WellKnownUI.Menus.File<br />

WellKnownUI.Menus.Edit<br />

WellKnownUI.Menus.View<br />

WellKnownUI.Menus.Insert<br />

WellKnownUI.Menus.Project<br />

WellKnownUI.Menus.Tools<br />

WellKnownUI.Menus.Window


Adding a New Menu Item<br />

WellKnownUI.Menus.Help<br />

WellKnownUI.Menus.MainMenu<br />

Figure 3-3 - WellKnownUI.Menus Static Read-only String Properties<br />

We want our new menu item to go in the Tools menu, so we specify<br />

the Tools field:<br />

myListSeismic.ParentKey = WellKnownUI.Menus.Tools;<br />

Image<br />

Optionally set the image for the menu item. You can use an image<br />

from Petrel available from the static class PetrelImages, or<br />

provide your own.<br />

myListSeismic.Image = PetrelImages.Seismic3D;<br />

Click Event Handler<br />

Finally we will specify what is going to happen when the menu item<br />

is clicked. Our menu item will run the same algorithm we wrote for<br />

our workstep.<br />

We need to add a click handler to our menu item instance:<br />

myListSeismic.Click += myListSeismicToolClick;<br />

The click event handler is a System.EventHandler and looks<br />

like:<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject.Seismic;<br />

private void myListSeismicToolClick(object sender, EventArgs e)<br />

{<br />

Project project = PetrelProject.PrimaryProject;<br />

SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />

SeismicProject seismicProj = seismicRoot.SeismicProject;<br />

foreach(SeismicCollection scol in<br />

seismicProj.SeismicCollections)<br />

{<br />

if (scol.SeismicCubeCount > 0)<br />

{<br />

foreach (SeismicCube cube in scol.SeismicCubes)<br />

{<br />

PetrelLogger.InfoOutputWindow(scol.Name +<br />

" contains Seismic Cube " +


Adding a New Menu Item<br />

}<br />

}<br />

}<br />

}<br />

cube.Name);<br />

Add to ToolsManager<br />

At this point, we have defined the new menu item (text, image,<br />

location and what will happen when the tool is clicked), but it is not<br />

in the Petrel user interface yet.<br />

The ToolsManager class provides functionality to add menu<br />

items. Use the Add method on ToolsManager to add the new<br />

menu item into the Petrel UI.<br />

public sealed class ToolsManager<br />

{<br />

public void Add (IToolbarItem item);<br />

...<br />

}<br />

We can access this <strong>Ocean</strong> service from a read only property of the<br />

convenience class PetrelSystem.Tools. We will add our new<br />

tool to the UI after the tool is created:<br />

PetrelSystem.Tools.Add(myListSeismic);<br />

Results<br />

Putting everything together, the example to add a new menu item<br />

into the Petrel Tools menu is as follows:<br />

public void IntegratePresentation ()<br />

{<br />

ToolbarItem myListSeismic = new ToolbarItem();<br />

64


Adding a New Menu Item<br />

}<br />

myListSeismic.ParentKey = WellKnownUI.Menus.Tools;<br />

myListSeismic.Text = "List Seismic Cubes";<br />

myListSeismic.Image = PetrelImages.Seismic3D;<br />

myListSeismic.Click += myListSeismicToolClick;<br />

PetrelSystem.Tools.Add(myListSeismic);<br />

void myListSeismicToolClick(object sender, EventArgs e)<br />

{<br />

Project project = PetrelProject.PrimaryProject;<br />

SeismicRoot seismicRoot = SeismicRoot.Get(project);<br />

SeismicProject seismicProj = seismicRoot.SeismicProject;<br />

}<br />

foreach(SeismicCollection scol in<br />

seismicProj.SeismicCollections)<br />

{<br />

if (scol.SeismicCubeCount > 0)<br />

{<br />

foreach (SeismicCube cube in scol.SeismicCubes)<br />

}<br />

}<br />

{<br />

}<br />

PetrelLogger.InfoOutputWindow(scol.Name +<br />

" contains Seismic Cube " +<br />

cube.Name);<br />

Now, after you rebuild your module and run Petrel, you will see a<br />

new menu item called “List Seismic Cubes” in the Petrel Tools<br />

menu:<br />

Figure 3-4 - New “List Seismic Cubes” Menu Item in Petrel Tools<br />

Menu


Chapter 3<br />

Extending the Data Domain<br />

<strong>Ocean</strong> allows you to customize and extend the Petrel application by<br />

creating new data objects, called custom domain objects. Custom<br />

domain objects can contribute implementations for, and participate<br />

in, standard Petrel behavior. They can be:<br />

• Added to the Input and Model trees<br />

• Displayed in a standard Petrel window<br />

You can decide what functionality to implement for a given custom<br />

domain object by deciding what interfaces (services) to implement<br />

and register with <strong>Ocean</strong>. All are optional in terms of <strong>Ocean</strong><br />

requirements.<br />

This chapter will describe how to write a simple custom domain<br />

object and have it participate in standard Petrel behavior.<br />

For details on these topics and more, please see the <strong>Ocean</strong><br />

Developers <strong>Guide</strong>.<br />

66


Extending the Data Domain<br />

Basic Custom Domain Object<br />

A custom domain object in Petrel is just a new class. The most<br />

basic custom domain object is a class that inherits from the .NET<br />

object, even when it doesn't implement any <strong>Ocean</strong> interfaces.<br />

Consider a simple object that has a position in space and a radius<br />

that defines its size.<br />

public class XYZObj<br />

{<br />

private float m_x = 100.0f;<br />

private float m_y = 100.0f;<br />

private float m_z = 100.0f;<br />

private float m_radius = 100.0f;<br />

}<br />

public float X<br />

{<br />

get { return m_x; }<br />

set { m_x = value; }<br />

}<br />

public float Y<br />

{<br />

get { return m_y; }<br />

set { m_y = value; }<br />

}<br />

public float Z<br />

{<br />

get { return m_z; }<br />

set { m_z = value; }<br />

}<br />

public float Radius<br />

{<br />

get { return m_radius; }<br />

set { m_radius = value; }<br />

}


Extending the Data Domain<br />

This basic object is a valid custom domain object that can<br />

participate in Petrel behavior. It can be added to the Petrel Input<br />

and Model trees.<br />

Adding to Input and Models Trees<br />

The Input tree and the Models tree support the addition of new<br />

nodes (domain objects) using the IInput and IModels interfaces.<br />

public interface IInput : ...<br />

{<br />

void Add(object item );<br />

}<br />

public interface IModels : ...<br />

{<br />

void Add(object item );<br />

}<br />

You can access these interfaces via the PetrelProject class.<br />

using Slb.<strong>Ocean</strong>.Petrel;<br />

using Slb.<strong>Ocean</strong>.Petrel.UI;<br />

PetrelProject.Inputs.Add( new XYZObj( ) );<br />

PetrelProject.Models.Add( new XYZObj( ) );<br />

Figure 4-1 – XYZ object in Input and Models trees.<br />

68


Extending the Data Domain<br />

Adding to Native Petrel Domain<br />

Objects<br />

Custom domain objects may be added as children to some native<br />

Petrel domain objects using the IExtensions interface:<br />

public interface IExtensions : IEnumerable, IEnumerable<br />

{<br />

void Add( object item );<br />

bool Remove( object item );<br />

bool Contains( object item );<br />

...<br />

}<br />

This example adds a XYZObj custom domain object to a<br />

Borehole, a native Petrel domain object. Start with a Borehole<br />

object.<br />

Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well.Borehole bore = ...;<br />

You must use a transaction when adding the children. The<br />

transaction is created by the DataManager API.<br />

using (ITransaction txn = DataManager.NewTransaction())<br />

We lock the Borehole since we are going to change its children.<br />

txn.Lock(bore);<br />

Now we add a new XYZObj using IExtensions.<br />

bore.Extensions.Add(new XYZObj());<br />

Finally the operation is completed by committing the transaction.<br />

txn.Commit();<br />

Putting it all together we have:<br />

Slb.<strong>Ocean</strong>.Petrel.DomainObject.Well.Borehole bore = ...;<br />

using (ITransaction txn = DataManager.NewTransaction())<br />

{<br />

txn.Lock(bore);<br />

bore.Extensions.Add(new XYZObj());<br />

txn.Commit();<br />

}


Extending the Data Domain<br />

70<br />

Figure 4-2 – XYZObj added to a Borehole object (A10).<br />

Adding an Object from a Context<br />

Menu<br />

All native Petrel objects have context menus that appear with a<br />

right mouse button click on the object in the tree. We will add our<br />

own context menu item to the Borehole context menu which will<br />

add a XYZObj as a child. Define a context menu item using the<br />

ContextMenuItem class:<br />

public class ContextMenuItem : IContextMenuItem,<br />

IPresentation where TDomainObject : class<br />

{<br />

public ContextMenuItem ( );<br />

public ContextMenuItem ( string text,<br />

ContextMenuEventHandler clickCallback );<br />

public string Text { get; set; }<br />

public event ContextMenuEventHandler Click;<br />

...<br />

}


Extending the Data Domain<br />

Add the new context menu item in the module’s<br />

IntegratePresentation method. First we create a Borehole<br />

context menu item, and then add it to the Petrel UI using the<br />

PetrelSystem.Tools service.<br />

ContextMenuItem cItem = new ContextMenuItem(<br />

“Insert XYZ object”, AddXYZClick );<br />

PetrelSystem.Tools.Add( cItem );<br />

An event handler must be provided to respond to the click of the<br />

context menu item. The AddXYZClick event handler will add the<br />

XYZObj object to the Borehole from the context menu.<br />

public void AddXYZClick( object sender,<br />

ContextMenuEventArgs e )<br />

{<br />

Borehole bore = e.ContextObject as Borehole;<br />

Completing our example, the event handler is now:<br />

public void AddXYZClick( object sender,<br />

ContextMenuEventArgs e )<br />

{<br />

// Get the borehole object<br />

Borehole bore = e.ContextObject as Borehole;<br />

if (bore == null)<br />

return;<br />

}<br />

using (ITransaction txn = DataManager.NewTransaction())<br />

{<br />

txn.Lock(bore);<br />

bore.Extensions.Add(new XYZObj());<br />

txn.Commit();<br />

}<br />

The new “Insert XYZ object” context menu item is added to the<br />

bottom of the Borehole context menu.


Extending the Data Domain<br />

Figure 4-3 – Context menu containing XYZ object.<br />

Customizing Tree Presentation<br />

A new custom domain object has a default presentation style when<br />

displayed in the tree. The icon is the <strong>Ocean</strong> bubbles icon, and the<br />

text is object.ToString.<br />

Figure 4-4 - Default Presentation in Tree<br />

72<br />

An object can control its appearance in the Petrel trees by<br />

implementing the Slb.<strong>Ocean</strong>.Petrel.UI.IPresentation<br />

interface.


Extending the Data Domain<br />

public interface IPresentation<br />

{<br />

public Bitmap Image { get; }<br />

public string Text { get; }<br />

public event EventHandler PresentationChanged;<br />

}<br />

IPresentation allows the custom domain object to provide a<br />

bitmap and a text string to be displayed in the user interface. It also<br />

provides an event that is raised when the presentation has<br />

changed.<br />

Implementing IPresentation for our XYZObj object gives us:<br />

using Slb.<strong>Ocean</strong>.Petrel.UI;<br />

public class XYZObj : IPresentation<br />

{<br />

public System.Drawing.Bitmap Image<br />

{<br />

get { return MyImageLibrary.XYZObjImage; }<br />

}<br />

}<br />

}<br />

public string Text<br />

{<br />

get { return “XYZ Object”; }<br />

}<br />

public event EventHandler PresentationChanged { add {} remove {}<br />

...<br />

In this case the object, MyImageLibrary, contains a resource for<br />

the bitmap displayed with the object name.<br />

Figure 4-5 - Custom Presentation of XYZ object


Extending the Data Domain<br />

Object Visualization<br />

A custom domain object may be rendered in standard Petrel<br />

windows. The 3D and 2D windows render data using OpenInventor<br />

(Oiv), while the Map and WellSection windows use .NET GDI+ to<br />

perform the rendering.<br />

3D Window Display<br />

Two steps must be performed to render a custom object in the 3D<br />

or 2D window.<br />

5. Implement an OpenInventor factory for the object type<br />

6. Add the factory service to <strong>Ocean</strong><br />

We implement the IOpenInventorFactory interface to provide<br />

the OpenInventor factory for displaying our object.<br />

public interface IOpenInventorFactory<br />

{<br />

bool CanCreate(object domainObj, OpenInventorContext c);<br />

SoNode Create(object domainObj, OpenInventorContext c);<br />

void Dispose(SoNode node, object dObj, OpenInventorContext c);<br />

void Update( SoNode node, object dObj, OpenInventorContext c);<br />

}<br />

We start by creating a class to implement<br />

IOpenInventorFactory.<br />

public class MyXYZObjOivFactory : IOpenInventorFactory<br />

74


Extending the Data Domain<br />

Next we implement the IOpenInventorFactory methods.<br />

CanCreate determines if the domain object can be rendered in the<br />

window. It can use properties of the object or the context to decide.<br />

public bool CanCreate( object o, OpenInventorContext ctx )<br />

{<br />

return true;<br />

}<br />

Create is called the first time an OpenInventor scenegraph is<br />

needed to display an object. It creates a new root node for the<br />

OpenInventor node structure and then calls the Update method to<br />

handle the display.<br />

public SoNode Create( object o, OpenInventorContext ctx )<br />

{<br />

SoSeparator root = new SoSeparator( );<br />

Update( root, o, ctx );<br />

return root;<br />

}<br />

The Update method actually builds the node hierarchy for the<br />

object being displayed. The position of the XYZObj object is<br />

translated into the window coordinates. A sphere is defined to<br />

represent it, and is added to the node hierarchy. Refer to<br />

OpenInventor documentation for details.<br />

public void Update( SoNode node, object o, OpeninventorContext ctx)<br />

{<br />

SoSeparator root = node as SoSeparator;<br />

root.RemoveAllChildren( );<br />

}<br />

// Set up the translation for our object<br />

XYZObj xyz = (XYZObj) o;<br />

SbVec3f v3 = ctx.WorldToWindow3D(xyz.X, xyz.Y, xyz.Z);<br />

SoTranslation trans = new SoTranslation();<br />

trans.translation.SetValue(v3);<br />

root.AddChild(trans);<br />

// Create a sphere with our objects radius<br />

SoSphere s = new SoSphere();<br />

s.radius.SetValue( xyz.Radius );<br />

root.AddChild(s);<br />

return;<br />

Dispose should clean up any resources associated with the<br />

scenegraph, but not the scenegraph itself.


Extending the Data Domain<br />

public void Dispose( SoNode n, object o, OpenInventorContext ctx )<br />

{<br />

}<br />

The complete OpenInventor factory class for rendering the XYZObj<br />

object is:<br />

using MC.Inventor;<br />

using MC.Inventor.Nodes;<br />

using Slb.<strong>Ocean</strong>.Petrel.DomainObject;<br />

using Slb.<strong>Ocean</strong>.Petrel.UI;<br />

public class MyXYZObjOivFactory : IOpenInventorFactory<br />

{<br />

public bool CanCreate( object o, OpenInventorContext ctx )<br />

{<br />

return true;<br />

}<br />

}<br />

public SoNode Create( object o, OpenInventorContext ctx )<br />

{<br />

SoSeparator root = new SoSeparator( );<br />

Update( root, o, ctx );<br />

return root;<br />

}<br />

public void Update(SoNode n, object o, OpeninventorContext ctx)<br />

{<br />

SoSeparator root = n as SoSeparator;<br />

root.RemoveAllChildren( );<br />

// Set up the translation<br />

XYZObj xyz = (XYZObj) o;<br />

SbVec3f v3 = ctx.WorldToWindow3D(xyz.X, xyz.Y, xyz.Z);<br />

SoTranslation trans = new SoTranslation( );<br />

trans.translation.SetValue( v3 );<br />

root.AddChild( trans );<br />

// Create a sphere using objects radius<br />

SoSphere s = new SoSphere( );<br />

s.radius.SetValue( xyz.Radius );<br />

root.AddChild( s );<br />

return;<br />

}<br />

public void Dispose(SoNode n, object o, OpenInventorContext ctx )<br />

{<br />

}<br />

The OpenInventor factory must be added as a service in the<br />

module’s Initialize method to display the XYZObj object.<br />

public void Initialize ( )<br />

{<br />

Type objType = typeof( XYZObj );<br />

Type factoryType = typeof( IOpenInventorFactory );<br />

XYZObjOivFactory factory = new XYZObjOivFactory( );<br />

CoreSystem.Services.AddService( objType, factoryType, factory);<br />

}<br />

76


Extending the Data Domain<br />

When a 3D window is active, the XYZObject object will have a<br />

checkbox next to it indicating that it may be displayed.<br />

Figure 4-6 – XYZ Object ready to display in 3D window.<br />

When the end user checks the checkbox the object will be<br />

displayed.<br />

Figure 4-7 – XYZObj object in 3D window<br />

Map Window Display<br />

Two steps are required to render the custom domain object in the<br />

Map window.<br />

Implement the IMapWindow factory interface.<br />

Add the factory to <strong>Ocean</strong> as a service.<br />

We implement the IMapRenderer interface to display the<br />

XYZObj.<br />

public interface IMapRenderer<br />

{<br />

void Initialize(object domainObj, MapRendererContext ctx);<br />

bool CanDraw (object domainObj, MapRendererContext ctx);<br />

void Draw (object domainObj, MapRendererContext ctx);<br />

Box2 GetBounds (object domainObj, MapRendererContext ctx);


Extending the Data Domain<br />

}<br />

void Dispose<br />

(object domainObj, MapRendererContext ctx);<br />

We start by defining the class that implements the IMapRenderer<br />

interface. We will render our object as a filled circle.<br />

public class XYZObjMapDisplay : IMapRenderer<br />

Next we implement the Initialize method. Our simple XYZObj<br />

has nothing to do.<br />

public void Initialize ( object o, MapRendererContext ctx )<br />

{<br />

return;<br />

}<br />

The CanDraw method reports whether or not the renderer knows<br />

how to draw the object. In our case it will always return true.<br />

public bool CanDraw ( object o, MapRendererContext ctx )<br />

{<br />

return true;<br />

}<br />

Draw renders a representation of the object into the window using<br />

the appropriate GDI+ drawing surfaces. It gets the world<br />

coordinates from the context for drawing. It defines the brush style<br />

and color and uses the properties of the XYZObj instance to draw<br />

a filled ellipse.<br />

public void Draw ( object o, MapRendererContext ctx )<br />

{<br />

// Get the World coordinates<br />

Graphics gworld = ctx.World;<br />

// Define the brush in blue<br />

using (Brush br = new SolidBrush(Color.Blue))<br />

{<br />

XYZObj xyz = (XYZObj)o;<br />

int x = (int)xyz.X;<br />

int y = (int)xyz.Y;<br />

int size = (int)xyz.Radius;<br />

78<br />

}<br />

}<br />

// Draw the circle<br />

gworld.FillEllipse(br, x-size, y-size, size*2, size*2);<br />

GetBounds computes the bounding box that the instance occupies<br />

and returns a Box2.<br />

public Box2 GetBounds ( object o, MapRendererContext ctx )<br />

{<br />

XYZObj xyz = (XYZObj)o;<br />

double x = xyz.X;


Extending the Data Domain<br />

}<br />

double y = xyz.Y;<br />

double size = xyz.Radius;<br />

Point2 begin = new Point2( x - size, y - size );<br />

Point2 end = new Point2( x + size, y + size );<br />

return new Box2( begin, end );<br />

Dispose has nothing to do for our simple XYZObj object.<br />

public void Dispose ( object o, MapRendererContext ctx )<br />

{<br />

return;<br />

}<br />

The complete IMapRenderer implementation for XYZObj is:<br />

using System.Drawing;<br />

using System.Drawing.Drawing2D;<br />

using Slb.<strong>Ocean</strong>.Geometry;<br />

using Slb.<strong>Ocean</strong>.Petrel.UI;<br />

public class XYZObjMapDisplay : IMapRenderer<br />

{<br />

public void Initialize ( object o, MapRendererContext ctx )<br />

{<br />

return;<br />

}<br />

public bool CanDraw ( object o, MapRendererContext ctx )<br />

{<br />

return true;<br />

}<br />

public void Draw ( object o, MapRendererContext ctx )<br />

{<br />

// Get the World coordinates<br />

Graphics gworld = ctx.World;<br />

using (Brush br = new SolidBrush(Color.Blue))<br />

{<br />

XYZObj xyz = (XYZObj)o;<br />

int x = (int)xyz.X;<br />

int y = (int)xyz.Y;<br />

int size = (int)xyz.Radius;<br />

}<br />

}<br />

// Draw the circle<br />

gworld.FillEllipse(br, x-size, y-size, size*2, size*2);<br />

public Box2 GetBounds ( object o, MapRendererContext ctx )<br />

{<br />

XYZObj xyz = (XYZObj)o;<br />

double x = xyz.X;<br />

double y = xyz.Y;<br />

double size = xyz.Radius;<br />

Point2 begin = new Point2( x - size, y - size );<br />

Point2 end = new Point2( x + size, y + size );<br />

}<br />

// Return a box that describes the area the object occupies<br />

return new Box2( begin, end );


Extending the Data Domain<br />

}<br />

public void Dispose ( object o, MapRendererContext ctx )<br />

{<br />

return;<br />

}<br />

The second step in rendering a custom domain object in a window<br />

is to register the drawing service with <strong>Ocean</strong> in the module’s<br />

Initialize method.<br />

Type xyzType = typeof(XYZObj);<br />

Type factoryType = typeof(IMapRenderer);<br />

XYZObjMapDisplay mapFactory = new XYZObjMapDisplay();<br />

CoreSystem.Services.AddService(xyzType, factoryType, mapFactory);<br />

When the end user checks the checkbox, the XYZObject is<br />

rendered in the Map window.<br />

Figure 4-8 – XYZ Object in Map window<br />

80


Extending the Data Domain<br />

Saving Custom Domain Objects<br />

A custom domain object may be saved into the Petrel project using<br />

serialization.<br />

Serialization<br />

Serialization has four steps.<br />

1. Mark the class with the Serializable attribute<br />

[Serializable]<br />

public class XYZObj : ...<br />

2. Implement the ISerializable interface<br />

public class XYZObj : ISerializable<br />

3. Implement GetObjectData to serialize the object<br />

public void GetObjectData( SerializationInfo info,<br />

StreamingContext ctx )<br />

{<br />

info.AddValue( “X”, m_x );<br />

info.AddValue( “Y”, m_y );<br />

info.AddValue( “Z”, m_z );<br />

info.AddValue( “Radius”, m_radius );<br />

}<br />

All of the properties which are serialized must be serializable.<br />

4. Write the deserialization constructor.<br />

public XYZObj (SerializationInfo info, StreamingContext ctx )<br />

{<br />

this.m_x = info.GetSingle(“X”);<br />

this.m_y = info.GetSingle(“Y”);<br />

this.m_z = info.GetSingle(“Z”);<br />

this.m_radius = info.GetSingle(“Radius”);


Extending the Data Domain<br />

}<br />

The XYZObj class declaration therefore becomes:<br />

[Serializable]<br />

public class XYZObj : ISerializable<br />

{<br />

float m_x;<br />

float m_y;<br />

float m_z;<br />

float m_radius;<br />

...<br />

// Deserialization Constructor<br />

public XYZObj(SerializationInfo info, StreamingContext ctx)<br />

{<br />

this.m_x = info.GetSingle(“X”);<br />

this.m_y = info.GetSingle(“Y”);<br />

this.m_z = info.GetSingle(“Z”);<br />

this.m_radius = info.GetSingle(“Radius”);<br />

}<br />

...<br />

public void GetObjectData(SerializationInfo info,<br />

StreamingContext context )<br />

{<br />

info.AddValue( “X”, m_x );<br />

info.AddValue( “Y”, m_y );<br />

info.AddValue( “Z”, m_z );<br />

info.AddValue( “Radius”, m_radius );<br />

}<br />

}<br />

When project is saved, the XYZObject will be saved with it.<br />

Reopening the project will load this data back into Petrel.<br />

82

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!