Innovating in Oil & Gas with SystemModeler

Eric Smith, Assured Flow Solutions
Houston, TX

Join the Conversation #WolframTechConf

Outline

Oil & Gas modeling

Software used

Consulting workflow

Problems

Why SystemModeler

Advantages of Modelica

Features of SystemModel (WSMLink)

Conclusions

Oil & Gas Simulators

Multiphase Simulators

Specializes in solid-liquid-liquid-gas flows

Precise hydrocarbon-water thermodynamics

Semi-rigorous heat transport

Transient models

Very expensive

Process models

Broader thermodynamics

Large process component library

Heat exchangers, distillation, flow splitting, pumps

Steady-state first

How consulting’s typically done

Set up a model

Define and run simulations

Could take several days using software that costs >$100k/year

Limited in scope - parameter matrix can’t be very large

No “math on models”

Manually – one by one – extract trends and profiles in Excel or O&G plotting software

No scripting

No efficient post processing

Write a report

Manual plot generation makes reprocessing expensive

Reports are static and updating plots are manual (Yes, word has embedding options)

Result:

Costs are higher than they need to be (IMO)

Analysis is shallower than they could be (wax example)

Oil & Gas models

Schlumberger

Multiphase model: OLGA

Process model: HYSYS

Kongsberg

Multiphase model: LedaFlow

Process model: Kspice

Wood

Multiphase model: PIMS

Process model: GMA

Others:

Pipesim, Impulse, Maximus, PetroSIM

Languages

*Based on the system I know best

Pipeline model - Fortran

Process models - C#

User interface - Visual Basic

Instruction

Visual Basic → C# → Fortran

Solvers

C# - 3rd party matrix solver

Fortran - Semi implicit Euler

Problems

Data flow (more on this later)

Documentation of configuration & simulations

Handling I/O (units)

Manually solve mass conservation

Pipeline to process model interface

Expensive to pay engineers to develop and debug solvers

Model is a house of cards

Every new feature potentially breaks the fundamentals

Data Flow

DynamicModule[{nFlags=0},TabView[{"Ideal"->Image[,ImageSize->Scaled[0.3]],"Details"->Image[,ImageSize->Scaled[0.7]],"Real Life"->(nFlags=0;Column@{Button["Add Flags",If[nFlags==0,nFlags=100,nFlags=0]],
Dynamic[
Show@@Flatten[{Image[,ImageSize->Scaled[0.7]],Graphics[Style[Text["FLAG",#],FontSize->24,FontColor->Red,Background->Lighter@Lighter@Gray]]&/@Block[{n=nFlags},Transpose[{RandomReal[1858,n],RandomReal[1154,n]}]]}]]})
}]]

Example problem: manifold splitting

Problem Theory

Assumptions and Nomenclature:
Presume nothing about the flow direction
	m[i-1,n-1,k] is a mass rate...
		at the previous time step (i-1)
		one section away from the node i.e. junction (n-1)
		and it is in the (k^th) branch

Since pressures are defined at the midpoint of a section, the pressure at the current time step at the node (1.5 sections away) is
	P[i,n,k]+fd[k] (f/2)(((((m[i,n,k])/(ρ A[k]))))^2)(L/2)
Where fd[k] is the flow direction of the (k^th) branch, if you move upstream from the node (manifold) to the midpoint of the branch then fd[k]=+1, else fd[k]=-1. Other way to say it: branches coming into the branch fd[k]=+1, branches coming out of the manifold fd[k]=-1

But now, to get P[i,n,k] (manifold pressure), we expand in time
	P[i,n,k]=P[i-1,n,k]+(dP/dt) dt
where (dP/dt) is the total derivative

totaldP=Dt[P[T,m,u],t]

Because temperature changes slowly dT/dt can be assumed zero, variations in mass rate due to temperature will be accounted for elsewhere in PIMS and reflected in m(i,n-1,k)

Also, since du/dt is the acceleration of the fluid, and the steady state frictional drop is already accounted for, this too can be neglected. However, in a cold start, this could be useful to dampen the convergence of the solution. 

Using the gas law with compressibility
	((∂P)/(∂m))=(∂/(∂m))(((m T Z R)/(MW V)))=((T Z R)/(MW V))
We are now left with...

branch[k_]=P[i-1,n,k]-fd[k] TZR[k]/MWV[k] ((m[i,n,k]+rel mold)/(rel+1)-m[i-1,n-1,k]) dt-fd[k] f[k] m[i-1,n-1,k] m[i,n,k]/(ρ Ac[k])^2 L[k]/2-fd[k]*BXDX[k]-fd[k] (simpDPA[k]+simpDPBC[k] m[i,n,k])

Though, it’s convenient to calculate the deviation from steady state flow, solving for “dm” instead of “m”. It looks like this...

(*branch[k_]=P[i-1,n,k]+fd[k] TZR[k]/MWV[k] dm[i,n,k] dt-fd[k] f[k] m[i-1,n-1,k] (m[i-1,n-1,k]+dm[i,n,k])+BXDX[k]*)

but I haven’t gotten that to work. For the rest of the notebook I'll be solving for the absolute mass rate in each branch

PIMS uses the Stoner pressure drop which uses (Pressure^2) terms. The equation below is the Stoner equation adopting some PIMS nomenclature
	gam - friction factor
	mm1 - split branch mass rate from previous step (mm1=mass minus 1 i.e. previous time step)
	bxdx - hydrostatic term
	pavg2 - 2*arithmetic average of the mid-section pressure and outlet/inlet pressure	
		i.e. (Subscript[P, manifold])+(Subscript[P, branch])

Friction only:

Solve[(Factor[pMid^2-pMan^2]/.Plus[pMid,pMan]->pavg2)==fd[k] m[i,n,k]^2 gam Sign[mm1]+bxdx,pMan][[1]]

The mass rate can’t change all that much from one section to another. Expand the quadratic friction term around “m1sec” or the mass rate next to the manifold mass rate for each branch

frictionExpansionWithSign=(fd[k] (Normal@Series[Sign[m[x]] m[x]^2 gam,{x,x0,1}] /.{m[x0]->m1sec[k],m'[x0]->fd[k] (m1sec[k]-m[i,n,k])/dx,Plus[Times[-1,x0],x]->dx}//Expand)/.Power[m1sec[k],2]->Times[Power[m1sec[k],2],Sign[m1sec[k]]]//Expand)/.Power[fd[k],2]->1//Collect[#,m[i,n,k]]&

“frictionExpansionWithSign” hasn’t been implemented. Currently (18-Oct-16), this is the expansion in Splitbranch:

frictionExpansion=(fd[k] (Normal@Series[m[x]^2 gam,{x,x0,1}] /.{m[x0]->m1sec[k],m'[x0]->fd[k] (m1sec[k]-m[i,n,k])/dx,Plus[Times[-1,x0],x]->dx}//Expand)/.Power[m1sec[k],2]->Times[Power[m1sec[k],2],Sign[m1sec[k]]]//Expand)/.Power[fd[k],2]->1//Collect[#,m[i,n,k]]&

The complete pressure drop for a section (excluding valves and including extrapolation in time) includes the friction (Stoner eqn), hydrostatic (bxdx), and dP/dm term.

The first equation for “pimsStyle” expands the pMid^2-pMan^2 term and replace (pMan+pMid) with “pavg2”

pimsStyle=((pMan/.Solve[(Factor[pMid^2-pMan^2]/.Plus[pMid,pMan]->pavg2)==fd[k] m[i,n,k]^2 gam Sign[mm1]+fd[k] bxdx+fd[k] (simpDPA[k]+simpDPBC[k] m[i,n,k]),pMan][[1]])-fd[k] dt TZR/MWV ((m[i,n,k]+rel mold)/(rel+1)-m[i-1,n-1,k]))/.Power[m[i,n,k],2]->mm1 (m[i,n,k])

Do the same expansion but include the “frictionExpansion” for m[i,n,k]^2

pimsStyle=((pMan/.Solve[(Factor[pMid^2-pMan^2]/.Plus[pMid,pMan]->pavg2)==frictionExpansion+fd[k] bxdx+fd[k] (simpDPA[k]+simpDPBC[k] m[i,n,k]),pMan][[1]])-fd[k] dt TZR/MWV ((m[i,n,k]+rel mold)/(rel+1)-m1sec[k]))/.Power[m[i,n,k],2]->mm1 (m[i,n,k])

Finally, group the terms so that the sum equals Pman. This will be a general equation for any branch “k”

branch[k_]=pimsStyle/.{TZR->TZR[k],mm1->m1sec[k],MWV->MWV[k],dm->dm[i,n,k],bxdx->BXDX[k],gam->f[k],pMid->P[i-1,n,k],pavg2->pavg2[k](*,m->m[i-1,n-1,k]*),Sign[mm1]->1,rel->0}

All pressures are the same at the node so, if there are K branches then there are K-1 pressure equations and one mass balance.

Hand written notes on solution procedure

Contortions:

Set up the matrix equation

The Hand written notes section shows what problem is solved and how to solve the matrix by reducing it to an “upper triangular” form. This section shows what the matrix looks like using “branch[k]” for a manifold with 4 branches.

The example manifold has 4 branches

kay=4;
massBalance=Sum[(m[i,n,k]) fd[k],{k,kay}]
pressureBalance=Collect[Expand@Table[branch[k]-branch[k+1],{k,kay-1}],m]

Make a vector of mass rates, and pull the coefficients of each of these mass rates from the above equations

mvec=Table[m[i,n,k],{k,kay}];
coeffRules=CoefficientRules[#,mvec]&/@Flatten[{pressureBalance,massBalance},1]

Substitute these coefficients into a matrix

aMat=(IdentityMatrix[kay]/.#&/@coeffRules)/.Thread[IdentityMatrix[kay]->0]/.{rel->0(*,fd[_]->-1*)};
aMat//MatrixForm

Take the non-coefficients (constants in the linear equations) and put them in the “b” vector

(bvec=Collect[-(ConstantArray[0,kay]/.#&/@coeffRules)/.ConstantArray[0,kay]->0,TZR[_]]/.rel->0)//TableForm

The “b” vector is messy. The next few cells are to make sure I’m simplifying it and putting it into Fortran properly

bElements=-Cases[(List@@bvec[[1]]),(-P[__,1]|Times[(fd|f)[1],___])]/.fd[_]->-1
Cases[(List@@bvec[[1]]),(P[__,2]|Times[(fd|f)[2],___])]/.fd[_]->-1;
(Plus@@%-Plus@@%%)-bvec[[1]]/.fd[_]->-1//Simplify

Plus@@bElements[[3;;4]]//Simplify

TreeForm[bvec[[1]],2]

Table[bvec[[1,i]],{i,Length@bvec[[1]]}]//TableForm

Cases[bvec[[1]],Times[_[___,2,___],___]]

Here is the full Ax=b system of equations:

Row[{aMat,{mvec}\[Transpose],"=",{bvec}\[Transpose]}]

Determining FlowSplittingParameter Matrix

Rules for splits and ManGasSource :

If forward flowing, set splits from upstream branches to downstream branches

If backflowing from one downstream branch to another downstream branch with no flow from upstream, use only manifold sources (Does split get used at all?)

If backflowing from downstream branch into another downstream branch with flow, add relative flow amount to upstream split so total flow out of upstream branch is greater than 1, but assign a negative split from upstream to the backflowing downstream branch so the sum of all the splits from the upstream branch is still 1.

For liquid systems: when backflowing in an upstream branch, send flow from forward flowing branches to the downstream branch and backflow is handled elsewhere. There's no way to set the split to a backflowing upstream branch in a liquid system

For liquid systems: when backflowing from downstream to upstream with no forward flowing branches, set splits equal to 1 in both directions. There's no way to split this flow (is that true Dale?)

For gas systems: when backflowing in an upstream branch... I don't know what to do... I don't think I can set a split from upstream to upstream branches.

Setting “SPLIT” in PIMS isn’t very straight forward, this section sets up the equations that will be used to set SPLIT.

Given a flowrate vector for each branch (q) and the flow direction of each branch (fd, 1 = branch coming into manifold, -1 = branch going out of manifold), it calculates “SPLIT”.

Here are some definitions for TotalFlow, BackFlow, ForwardFlow, and the split matrix Sij

totalFlow[q_,fd_]:=Max[0,#]&/@(q*fd)//Total
backFlowQ[q_,fd_]:=Max[0,#]&/@((Min[0,#]&/@fd) q)
backFlow[q_,fd_]:=backFlowQ[q,fd]//Total
fwdFlowQ[q_,fd_]:=Max[0,#]&/@((Max[0,#]&/@fd) q);
fwdFlow[q_,fd_]:=fwdFlowQ[q,fd]//Total
upstreamBackFlowQ[q_,fd_]:=-Min[0,#]&/@((Max[0,#]&/@fd) q)
upstreamBackFlow[q_,fd_]:=upstreamBackFlowQ[q,fd]//Total
downstreamFwdFlowQ[q_,fd_]:=-Min[0,#]&/@((Min[0,#]&/@fd) q)
downstreamFwdFlow[q_,fd_]:=downstreamFwdFlowQ[q,fd]//Total
fixQ[q_,fd_]:=Module[{mostQ,mostFd},
mostQ=Most@q;
mostFd=Most@fd;
Append[mostQ,-Last@fdTemp Total[mostQ mostFd]]]
Sij[q_,fd_]:=Module[{qT,qB,qF,qUB,qDF,qLocal,someFlows},
qLocal=If[fwdFlow[q,fd]==0,-q,q];
someFlows={qT,qB,qF}=Through[{totalFlow,backFlow,fwdFlow}[qLocal,fd]];
qUB=upstreamBackFlow[qLocal,fd];
qDF=downstreamFwdFlow[q,fd];
(*ManGasFlag scenario when fwdFlow still equals zero*)
If[qF==0,qB=0;qLocal=Max[0,#]&/@qLocal];
If[qUB==0,
SijWhenNoUpstreamBackflow[qLocal,fd,someFlows],
If[qDF==0,
SijWithNoDownstreamFF[q,fd,someFlows],
SijWhenUpstreamBackflows[qLocal,fd,someFlows]]
]
]
SijWhenNoUpstreamBackflow[q_,fd_,{qT_,qB_,qF_}]:=Module[{qLocal},
Table[
If[fd[[i]]>0&&q[[i]]>0,
Table[Boole[fd[[j]]<0] q[[j]]/qF,{j,Length@q}],
q 0],{i,Length@q}]//N]
SijWhenUpstreamBackflows[q_,fd_,{qT_,qB_,qF_}]:=Module[
{qPos,someFlows,qBackSplit,sijPos,negativeSplits,sijFwdFlow,qRemaining,sijRemaining},
qPos=Max[0,#]&/@q;
someFlows=Through[{totalFlow,backFlow,fwdFlow}[qPos,fd]];
(*Construct Sij for positive splits from upstream to downstream*)
If[qF>downstreamFwdFlow[q,fd],
qPos=downstreamFwdFlowQ[q,fd] qF/downstreamFwdFlow[q,fd]+fwdFlowQ[q,fd]];
sijPos=SijWhenNoUpstreamBackflow[qPos,fd,someFlows];
(*Construct Sij for negative splits from upstream to downstream*)
negativeSplits=1-(Total/@sijPos)/.(1.0->0);
qBackSplit={negativeSplits}\[Transpose] .{backFlowQ[q,fd]/qB};
(*Add the Sij's together*)
sijFwdFlow=qBackSplit+sijPos;
(*Subtract forward Sij from flow vector*)
qRemaining=q-sijFwdFlow\[Transpose].q-fwdFlowQ[q,fd];
sijRemaining=Sij[qRemaining,fd];(*recursively call Sij*)
(*Add the backflow Sij to the forward flow Sij*)
sijRemaining+sijFwdFlow
]
SijWithNoDownstreamFF[q_,fd_,{qT_,qB_,qF_}]:=Module[
{qBv,qFv,revBFv,temp,sij,ffSplit},
qBv=backFlowQ[q,fd];
qFv=fwdFlowQ[q,fd];
revBFv=downstreamFwdFlowQ[qB-qBv,fd];
If[Total@revBFv==0,
(*all downstream backflow comes from one branch, assign 1 to each upstream Int*)
sij=Boole@Apply[#1>0&&#2>0&,Outer[List,fwdFlowQ[q,fd],backFlowQ[q,fd]],{2}],
(*Need to assign splits to downstream branches*)
temp = ConstantArray[revBFv/Total[revBFv],Length@q];
sij = (Boole[#>0&/@qFv] #&/@Transpose[temp])\[Transpose];
ffSplit=(qFv+sij\[Transpose].q)-q;
sij=Sij[ffSplit,fd]+sij];
sij
]

Create an array of flow rates and flow directions to test the main “Sij” routine. The “fixQ” function will make sure there’s a mass balance; this is needed since qTemp is a user input and could contain a mass balance error

qTemp={-.68,.032,231.5,-1};
fdTemp={1,1,-1,-1};
qTemp=fixQ[qTemp,fdTemp]

See what the split matrix looks like. This is the same as “FlowSplitting parameters.

Sij[qTemp,fdTemp](*//Rationalize*)

Try other flow and flow direction vectors:

qTemp={1,-3,-2};
fdTemp={1,1,-1};
qTemp=fixQ[qTemp,fdTemp];
tempSij=Sij[qTemp,fdTemp]
{qTemp,Total/@%,
Total/@(MapThread[Times,{tempSij,qTemp}]\[Transpose])}//TableForm

Yet another set

qTemp={1,4,-3,2,2};
fdTemp={1,-1,-1,1,-1};
qTemp=fixQ[qTemp,fdTemp]
tempSij=Sij[qTemp,fdTemp]
Total/@%
Total/@(MapThread[Times,{tempSij,qTemp}]\[Transpose])

This is from a project I was working on:

qDebug="10.33311
9.116083
11.42558
19.15817
803.2849
7.705246
7.607984
-737.9387"//StringSplit//ToExpression;
fdDebug= {1,1,1,1,-1,1,1,-1};
fDebug=fixQ[qDebug,fdDebug]

Change “mangasflag” (honestly - EWS 11/14/17 - I’m not sure what I’m testing here, would have to review the code)

mangasflag=False

Sij[qDebug,fdDebug]
Total/@%

For a hypothetical manifold, set q and fd. Since the mass has to be conserved, the last value of the flowrate will be replaced when “fixQ” is called

qTemp={-4,-2,-3,-3};
fdTemp={1,1,-1,-1};
qTemp=fixQ[qTemp,fdTemp]

Here’s a simplified form of the main Split calculation which is found in the definition of Sij

(q[j]/qT+qB q[j]/qT q[i]/qF)//Simplify

Here’s the Total, Back, and Forward flows for the made up manifold flow rates

Through[{totalFlow,backFlow,fwdFlow,upstreamBackFlow}[qTemp,fdTemp]]

Calculate the split matrix and also output the sum of each branch, each incoming branch should add up to 1.

Sij[qTemp,fdTemp]
Total/@%

qTemp={0,2,2,0};
fdTemp={1,1,-1,-1};
qTemp=fixQ[qTemp,fdTemp]
Through[{totalFlow,backFlow,fwdFlow}[qTemp,fdTemp]]
Sij[qTemp,fdTemp]
Total/@%

This simple mass balance equation is used somewhere in the code, I forget where

eqns[n_]:=Prepend[Most@Table[qfrac[i]==q[i]/Sum[q[k],{k,n}],{i,n}],qB+Sum[q[i],{i,n}]==1]

eqns[4]

Solve[eqns[2],Table[q[i],{i,2}]]

Solve[eqns[3],Table[q[i],{i,3}]]

Solve[eqns[4],Table[q[i],{i,4}]]//Simplify

Sij functions with outputs to debug fortran

This section was used to debug the different subroutines called within Fortran. I’m not sure it’s needed now

qTemp=StringReplace[StringCases["\t\tsplit(1)\t2.000000\tREAL(4) 
\t\tsplit(2)\t-3.013324\tREAL(4) 
\t\tsplit(3)\t136.2112\tREAL(4) 
\t\tsplit(4)\t-137.2246\tREAL(4) 
","\tsplit("~~_~~")\t"~~Shortest[x__]~~"\tREAL":>x],"E"->"1*^"]//ToExpression

SijWhenUpstreamBackflows[qTemp,fdTemp,Through[{totalFlow,backFlow,fwdFlow}[qTemp,fdTemp]]]

SijWhenNoUpstreamBackflow[q_,fd_,{qT_,qB_,qF_}]:=Module[{qLocal},
Table[
If[fd[[i]]>0&&q[[i]]>0,
Table[Boole[fd[[j]]<0] q[[j]]/qF,{j,Length@q}],
q 0],{i,Length@q}]//N]

SijWhenUpstreamBackflows[q_,fd_,{qT_,qB_,qF_}]:=Module[
{qPos,someFlows,qBackSplit,sijPos,negativeSplits,sijFwdFlow,qRemaining,sijRemaining},
qPos=Max[0,#]&/@q;
someFlows=Through[{totalFlow,backFlow,fwdFlow}[qPos,fd]];
(*Construct Sij for positive splits from upstream to downstream*)
If[qF>downstreamFwdFlow[q,fd],
qPos=downstreamFwdFlowQ[q,fd] qF/downstreamFwdFlow[q,fd]+fwdFlowQ[q,fd]];
sijPos=SijWhenNoUpstreamBackflow[qPos,fd,someFlows];
Print[Row@{"sijPos = ",sijPos}];
(*Construct Sij for negative splits from upstream to downstream*)
negativeSplits=1-(Total/@sijPos)/.(1.0->0);
Print[Row@{"NegativeSplits = ",negativeSplits}];
qBackSplit={negativeSplits}\[Transpose] .{backFlowQ[q,fd]/qB};
Print[Row@{"qBackSplit = ",qBackSplit}];
(*Add the Sij's together*)
sijFwdFlow=qBackSplit+sijPos;
Print[Row@{"sijFwdFlow = ",sijFwdFlow}];
(*Subtract forward Sij from flow vector*)
qRemaining=q-sijFwdFlow\[Transpose].q-fwdFlowQ[q,fd];
Print[Row@{"qRemaining = ",qRemaining}];
sijRemaining=Sij[qRemaining,fd];(*recursively call Sij*)
Print[Row@{"sijRemaining = ",sijRemaining}];
(*Add the backflow Sij to the forward flow Sij*)
sijRemaining+sijFwdFlow
]

Sij[q_,fd_]:=Module[{qT,qB,qF,qUB,qDF,qLocal,someFlows},
qLocal=If[fwdFlow[q,fd]==0,-q,q];
someFlows={qT,qB,qF}=Through[{totalFlow,backFlow,fwdFlow}[qLocal,fd]];
qUB=upstreamBackFlow[qLocal,fd];
qDF=downstreamFwdFlow[q,fd];
(*ManGasFlag scenario when fwdFlow still equals zero*)
If[qF==0,qB=0;qLocal=Max[0,#]&/@qLocal];
If[qUB==0,
Print["No Upstream Backflow"];
SijWhenNoUpstreamBackflow[qLocal,fd,someFlows],
If[qDF==0,
Print["No Downstream Back Flows"];
SijWithNoDownstreamFF[q,fd,someFlows],
Print["Upstream Back Flows"];
SijWhenUpstreamBackflows[qLocal,fd,someFlows]]
]
]

dPdm multiplier fit

In an attempt to make ManifoldCompressibility auto-correct the dP/dm calculation, I tried using the curve and corresponding equation. I don’t think it worked very well. I fixed some issues with the solver and matrix and now don’t really looked at ManifoldCompressibilityFactor

toFit={1,3/2} #&/@{{.01,0.00001},{.05,.02},{.1,.05},{.2,.1},{.3,.15},{.9,.19},{.99,.199}}

tryThis[x_]=a Log[b x+c]/.FindFit[toFit,a Log[b x+c],{a,b,c},x]

Show[ListLinePlot[toFit],Plot[tryThis[x],{x,0,1}]]

toFit=MapAt[Log10,#,1]&/@{{.01,0.00001},{.05,.02},{.1,.05},{.2,.1},{.3,.15},{.9,.19},{.99,.199}}

FindFormula[toFit,x]

ListLinePlot[toFit]

So... why did I leave my job?

SystemModeler (Modelica) advantages

Advantages specific to SystemModeler - “Math on models”

Optimization

TabView[{"Problem"->,"Solution"->}]

Scriptable configuration

Scriptable Hierarchical Modeling

Sensitivity analysis

Autotuning

Other issues solved

Modeling

Native connections to OPC

Robust stable connections to external models via FMU

Excel-FMU integration for engineers that refuse to learn ML

Cloud integration (still in development)

Coding

Maintainable

scalable (causality handled for each item)

scalable (object oriented, hierarchical)

Results

Parameter studies are easily scripted,

results are analyzed programmatically

Interactive results via CDF

Better visualizations

Potential for stochastic, monte-carlo or machine learning analysis

Conclusions

SystemModeler is a clear solution for many of the problems facing flow assurance and process engineering consulting

Issue: How do we convince a risk averse industry to try something new?

Collaboration: How can AFS help get the message out to oil & gas about Wolfram technologies?