diff options
| author | mo k <mo@mokhan.ca> | 2012-04-12 20:47:34 -0600 |
|---|---|---|
| committer | mo k <mo@mokhan.ca> | 2012-04-12 21:07:24 -0600 |
| commit | 071d432d0f399030e38a4543741a3b108f133acb (patch) | |
| tree | 24d9249a64109527ead255984688b8e1fcc29158 | |
| parent | 4670e5f5ef961c3d091e41fd7ad3ce4d3bd4b236 (diff) | |
calculate net and gross estimated reserves.
| -rw-r--r-- | src/domain/Well.cs | 470 | ||||
| -rwxr-xr-x | src/domain/domain.csproj | 1 | ||||
| -rw-r--r-- | src/test/WellSpecs.cs | 156 |
3 files changed, 522 insertions, 105 deletions
diff --git a/src/domain/Well.cs b/src/domain/Well.cs new file mode 100644 index 0000000..f0400a5 --- /dev/null +++ b/src/domain/Well.cs @@ -0,0 +1,470 @@ +namespace domain +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public class Gas : ICommodity + { + public Percent PercentageFrom(IComposition composition) + { + return composition.PercentageFor<Gas>(); + } + } + + public class Oil : ICommodity + { + public Percent PercentageFrom(IComposition composition) + { + return composition.PercentageFor<Oil>(); + } + } + + public class NGL : ICommodity + { + public Percent PercentageFrom(IComposition composition) + { + return composition.PercentageFor<NGL>(); + } + } + + public class Condensate : ICommodity + { + public Percent PercentageFrom(IComposition composition) + { + return composition.PercentageFor<Condensate>(); + } + } + + public class All : ICommodity + { + public Percent PercentageFrom(IComposition composition) + { + return composition.PercentageFor<Gas>() + .Plus(composition.PercentageFor<Oil>()) + .Plus(composition.PercentageFor<NGL>()) + .Plus(composition.PercentageFor<Condensate>()); + } + } + + public interface ICommodity + { + Percent PercentageFrom(IComposition composition); + } + + public class DrillSchedule + { + ICollection<IWell> wells = new List<IWell>(); + + public void Include(IWell well) + { + wells.Add(well); + } + + public IQuantity EstimatedGrossProductionFor<Commodity>(Month month) where Commodity : ICommodity, new() + { + IQuantity result = new Quantity(0, new BOED()); + Accept(well => + { + result = result.Plus(well.GrossProductionFor<Commodity>(month)); + }); + return result; + } + + public IQuantity EstimatedNetProductionFor<Commodity>(Month month) where Commodity : ICommodity, new() + { + IQuantity result = new Quantity(0, new BOED()); + Accept(well => + { + result = result.Plus(well.NetProductionFor<Commodity>(month)); + }); + return result; + } + + void Accept(Action<IWell> visitor ) + { + wells.Each(well => + { + visitor(well); + }); + } + } + + public interface IComposition + { + void SplitFor<Commodity>(Percent percent) where Commodity : ICommodity; + IQuantity PercentageOf<Commodity>(IQuantity quantity) where Commodity : ICommodity, new(); + Percent PercentageFor<Commodity>() where Commodity : ICommodity; + } + + public class DeclineCurve + { + IDictionary<int, IQuantity> production = new Dictionary<int, IQuantity>(); + IComposition split = new CommoditySplits(); + + public void Add(int month, IQuantity quantity) + { + production[month] = quantity; + } + + public TypeCurve StartingOn(Month initialProductionMonth) + { + return new TypeCurve(CreateProductionFor(initialProductionMonth)); + } + + IEnumerable<Production> CreateProductionFor(Month initialProductionMonth) + { + foreach (var quantity in production) + yield return new Production(initialProductionMonth.Plus(quantity.Key), quantity.Value, split); + } + + public void Composition<Commodity>(Percent percent) where Commodity : ICommodity + { + split.SplitFor<Commodity>(percent); + } + } + + public class CommoditySplits : IComposition + { + IDictionary<Type, Percent> splits = new Dictionary<Type, Percent>(); + + public void SplitFor<Commodity>(Percent percent) where Commodity : ICommodity + { + splits[typeof (Commodity)] = percent; + } + + public IQuantity PercentageOf<Commodity>(IQuantity quantity) where Commodity : ICommodity, new() + { + return new Commodity().PercentageFrom(this).Reduce(quantity); + } + + public Percent PercentageFor<Commodity>() where Commodity : ICommodity + { + return splits.ContainsKey(typeof(Commodity)) ? splits[typeof (Commodity)] : Percent.Zero; + } + } + + public class Production + { + Month month; + IQuantity produced; + IComposition split; + + public Production(Month month, IQuantity produced, IComposition split) + { + this.month = month; + this.produced = produced; + this.split = split; + } + + public bool IsFor(Month otherMonth) + { + return month.Equals(otherMonth); + } + + public IQuantity ProductionOf<T>() where T : ICommodity, new() + { + return split.PercentageOf<T>(produced); + } + } + + public class TypeCurve + { + IEnumerable<Production> production; + + public TypeCurve(IEnumerable<Production> production) + { + this.production = production.ToList(); + } + + public IQuantity ProductionFor<Commodity>(Month month) where Commodity : ICommodity, new() + { + return production.Single(x => x.IsFor(month)).ProductionOf<Commodity>(); + } + } + + public interface IQuantity + { + IQuantity Plus(IQuantity other); + IQuantity ConvertTo(IUnitOfMeasure units); + decimal Amount { get; } + IUnitOfMeasure Units { get; } + } + + public class Oppurtunity + { + Percent workingInterest; + DeclineCurve declineCurve; + + public Oppurtunity() + { + workingInterest = 100m.Percent(); + } + + public void WorkingInterest(Percent percent) + { + workingInterest = percent; + } + + public void DeclinesUsing(DeclineCurve declineCurve) + { + this.declineCurve = declineCurve; + } + + public IWell BringOnlineOn(Month initialProductionMonth) + { + return new Well(initialProductionMonth, workingInterest, declineCurve.StartingOn(initialProductionMonth)); + } + + public IEnumerable<IWell> BringOnlineOn(Month initialProductionMonth, int numberOfWells) + { + for (var i = 0; i < numberOfWells; i++) + yield return BringOnlineOn(initialProductionMonth); + } + } + + public class Well : IWell + { + Month initialProductionMonth; + Percent workingInterest; + TypeCurve curve; + + public Well(Month initialProductionMonth, Percent workingInterest, TypeCurve curve) + { + this.initialProductionMonth = initialProductionMonth; + this.workingInterest = workingInterest; + this.curve = curve; + } + + public IQuantity GrossProductionFor<Commodity>(Month month) where Commodity : ICommodity, new() + { + return curve.ProductionFor<Commodity>(month); + } + + public IQuantity NetProductionFor<Commodity>(Month month) where Commodity : ICommodity, new() + { + return workingInterest.Reduce(GrossProductionFor<Commodity>(month)); + } + } + + public class Month + { + readonly int year; + readonly int month; + + public Month(int year, int month) + { + this.year = year; + this.month = month; + } + + public bool Equals(Month other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.year == year && other.month == month; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (Month)) return false; + return Equals((Month) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (year*397) ^ month; + } + } + + public Month Plus(int months) + { + var newMonth = new DateTime(year, month, 01).AddMonths(months); + return new Month(newMonth.Year, newMonth.Month); + } + + public override string ToString() + { + return string.Format("{0} {1}", year, month); + } + } + + public interface IWell + { + IQuantity GrossProductionFor<T>(Month month) where T : ICommodity, new(); + IQuantity NetProductionFor<T>(Month month) where T : ICommodity, new(); + } + + public class Percent + { + readonly decimal percentage; + public static Percent Zero = new Percent(0); + + public Percent(decimal percentage) + { + this.percentage = percentage; + } + + public bool Equals(Percent other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.percentage == percentage; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (Percent)) return false; + return Equals((Percent) obj); + } + + public override int GetHashCode() + { + return percentage.GetHashCode(); + } + + public IQuantity Reduce(IQuantity original) + { + //return new ProratedQuantity(original, this); + return new Quantity(PortionOf(original.Amount), original.Units); + } + + public Percent Plus(Percent other) + { + return new Percent(percentage + other.percentage); + } + + public decimal PortionOf(decimal amount) + { + return amount*percentage; + } + + public override string ToString() + { + return string.Format("{0} %", percentage); + } + } + + public static class Units + { + public static Percent Percent(this decimal percentage) + { + return new Percent(percentage/100); + } + + public static IQuantity BOED(this int quantity) + { + return BOED(Convert.ToDecimal(quantity)); + } + + public static IQuantity BOED(this decimal quantity) + { + return new Quantity(quantity, new BOED()); + } + } + + public class BOED : IUnitOfMeasure + { + public decimal Convert(decimal amount, IUnitOfMeasure units) + { + // need to do actual conversion here; + return amount; + } + + public bool Equals(BOED other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return true; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (BOED)) return false; + return Equals((BOED) obj); + } + + public override int GetHashCode() + { + return (name != null ? name.GetHashCode() : 0); + } + + public override string ToString() + { + return name; + } + + readonly string name = "BOED"; + } + + public class Quantity : IQuantity + { + public Quantity(decimal amount, IUnitOfMeasure units) + { + Amount = amount; + Units = units; + } + + public decimal Amount { get;private set; } + + public IUnitOfMeasure Units { get; private set; } + + public IQuantity Plus(IQuantity other) + { + return new Quantity(Amount + other.ConvertTo(Units).Amount, Units); + } + + public IQuantity ConvertTo(IUnitOfMeasure unitOfMeasure) + { + return new Quantity(unitOfMeasure.Convert(Amount, this.Units), unitOfMeasure); + } + + public override string ToString() + { + return string.Format("{0} {1}", Amount, Units); + } + + public bool Equals(Quantity other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.Amount == Amount && Equals(other.Units, Units); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof (Quantity)) return false; + return Equals((Quantity) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (Amount.GetHashCode()*397) ^ (Units != null ? Units.GetHashCode() : 0); + } + } + } + + public interface IUnitOfMeasure + { + decimal Convert(decimal amount, IUnitOfMeasure units); + } + public static class Iterating + { + public static void Each<T>(this IEnumerable<T> items, Action<T> visitor){ + foreach (var item in items ?? Enumerable.Empty<T>()) + visitor(item); + } + } +} diff --git a/src/domain/domain.csproj b/src/domain/domain.csproj index ad902aa..5b74a53 100755 --- a/src/domain/domain.csproj +++ b/src/domain/domain.csproj @@ -41,6 +41,7 @@ </ItemGroup>
<ItemGroup>
<Compile Include="Greeting.cs" />
+ <Compile Include="Well.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition=" '$(OS)' == 'Windows_NT' " />
diff --git a/src/test/WellSpecs.cs b/src/test/WellSpecs.cs index afdaecd..c6a8a7b 100644 --- a/src/test/WellSpecs.cs +++ b/src/test/WellSpecs.cs @@ -1,105 +1,51 @@ -namespace test -{ - using System.Collections.Generic; - using Machine.Specifications; - using domain; - - public class WellSpecs - { - Establish context = ()=> - { - //sut = new Well(); - }; - - public class when_projecting_production_from_a_new_well - { - It should_calculate_the_correct_projection =()=> - { - var well = new Well(new Month(2013, 01)); - well.apply_split_for<Oil>(25.Percent()); - well.apply_split_for<Gas>(25.Percent()); - well.apply_split_for<NGL>(25.Percent()); - well.apply_split_for<Condensate>(25.Percent()); - - var decline = new DeclineCurve(); - decline.for_month(1,new BOED(100)); - well.use(decline); - - well.production_for<Oil>(new Month(2013, 01)).ShouldEqual(new BOED(25)); - }; - } - - static readonly Well sut; - } - public class DeclineCurve{ - IDictionary<int, BOED> curve; - public DeclineCurve(){ - curve = new Dictionary<int, BOED>(); - } - public void for_month(int month, BOED volume){ - curve[month] = volume; - } - //public void each(Action<int, BOED> action){ - //} - } - - public class BOED{ - decimal volume; - public BOED(decimal volume){ - this.volume = volume; - } - public bool Equals(BOED other){ - if(ReferenceEquals(null, other)) return false; - return other.volume == this.volume; - } - public override bool Equals(object other){ - if(ReferenceEquals(null, other)) return false; - if(!(other is BOED)) return false; - return Equals((BOED)other); - } - public override string ToString(){ - return string.Format("{0} BOED", volume); - } - } - public class Well - { - Month ip_month; - IDictionary<ICommodity, Percent> splits; - IList<Projection> projections; - - public Well(Month expected_initial_production_month){ - this.ip_month = expected_initial_production_month; - this.splits = new Dictionary<ICommodity, Percent>(); - this.projections = new List<Projection>(); - } - public void apply_split_for<Product>(Percent percent) where Product : ICommodity, new() - { - splits.Add(new Product(), percent); - } - public void use(DeclineCurve curve){ - //curve.each( (month,total_volume) => { - //projections.Add( splits.Select((split)=> split.create_projection(ip_month.add(month), total_volume)); - //}); - } - public BOED production_for<Commodity>(Month month){ - return new BOED(25); - } - } - public class Projection{} - public class Percent - { - } - public class Month{ - public Month(int year, int month){} - } - public interface ICommodity{} - public class Oil : ICommodity{} - public class Gas : ICommodity{} - public class NGL : ICommodity{} - public class Condensate : ICommodity{} - public static class Conversion{ - public static Percent Percent(this int percent){ - return new Percent(); - } - } -} +namespace test
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using Machine.Specifications;
+ using domain;
+
+ public class WellSpecs
+ {
+ public class when_estimating_production
+ {
+ It should_be_able_to_tell_the_estimated_total_production_for_any_month= () =>
+ {
+ var parkland100Percent = new Oppurtunity();
+ parkland100Percent.WorkingInterest(100m.Percent());
+ var declineCurve = new DeclineCurve();
+ declineCurve.Composition<Gas>(100m.Percent());
+ declineCurve.Add(0, 100.BOED());
+ parkland100Percent.DeclinesUsing(declineCurve);
+
+ var schedule = new DrillSchedule();
+ var jan2013 = new Month(2013, 01);
+ schedule.Include(parkland100Percent.BringOnlineOn(jan2013));
+ schedule.EstimatedGrossProductionFor<All>(jan2013).ShouldEqual(100.BOED());
+ schedule.EstimatedGrossProductionFor<Gas>(jan2013).ShouldEqual(100.BOED());
+ schedule.EstimatedGrossProductionFor<Oil>(jan2013).ShouldEqual(0.BOED());
+ schedule.EstimatedGrossProductionFor<NGL>(jan2013).ShouldEqual(0.BOED());
+ schedule.EstimatedGrossProductionFor<Condensate>(jan2013).ShouldEqual(0.BOED());
+ };
+
+ It should_be_able_to_tell_the_estimated_net_total_production_for_any_month = () =>
+ {
+ var parkland75Percent = new Oppurtunity();
+ parkland75Percent.WorkingInterest(75m.Percent());
+ var declineCurve = new DeclineCurve();
+ declineCurve.Composition<Gas>(50m.Percent());
+ declineCurve.Composition<Oil>(50m.Percent());
+ declineCurve.Add(0, 100.BOED());
+ parkland75Percent.DeclinesUsing(declineCurve);
+
+ var schedule = new DrillSchedule();
+ var jan2013 = new Month(2013, 01);
+ schedule.Include(parkland75Percent.BringOnlineOn(jan2013));
+ schedule.EstimatedNetProductionFor<All>(jan2013).ShouldEqual(75.BOED());
+ schedule.EstimatedNetProductionFor<Gas>(jan2013).ShouldEqual(37.5m.BOED());
+ schedule.EstimatedNetProductionFor<Oil>(jan2013).ShouldEqual(37.5m.BOED());
+ };
+ }
+ }
+}
|
