예제 #1
0
        public static bool ConsumerCanBeAdded(IList <ITimeSpaceInput> existingConsumers, ITimeSpaceInput consumer)
        {
            bool canBeAdded = true;

            if (existingConsumers.Count > 0)
            {
                // TODO (JG): make instead check on exchangeItem.ElementSet instead of an arbitray elementSet
                // check if #elements are consistent
                if (consumer.ElementSet() != null &&
                    existingConsumers[0].ElementSet() != null &&
                    consumer.ElementSet().ElementCount != existingConsumers[0].ElementSet().ElementCount
                    )
                {
                    canBeAdded = false;
                }
                // TODO (JG): Is this a requirement?
                if (consumer.TimeSet != null &&
                    existingConsumers[0].TimeSet != null &&
                    consumer.TimeSet.HasDurations != existingConsumers[0].TimeSet.HasDurations
                    )
                {
                    canBeAdded = false;
                }
                // check if #time stamps are consistent
            }
            return(canBeAdded);
        }
 public static bool ConsumerCanBeAdded(IList<ITimeSpaceInput> existingConsumers, ITimeSpaceInput consumer)
 {
     bool canBeAdded = true;
     if (existingConsumers.Count > 0)
     {
         // TODO (JG): make instead check on exchangeItem.ElementSet instead of an arbitray elementSet
         // check if #elements are consistent
         if (consumer.ElementSet() != null &&
             existingConsumers[0].ElementSet() != null &&
             consumer.ElementSet().ElementCount != existingConsumers[0].ElementSet().ElementCount
             )
         {
             canBeAdded = false;
         }
         // TODO (JG): Is this a requirement?
         if (consumer.TimeSet != null &&
             existingConsumers[0].TimeSet != null &&
             consumer.TimeSet.HasDurations != existingConsumers[0].TimeSet.HasDurations
             )
         {
             canBeAdded = false;
         }
         // check if #time stamps are consistent
     }
     return canBeAdded;
 }
예제 #3
0
    public void CreateInitializeTest()
    {
      ITimeSpaceComponent gwModel = new GWModelLC();
      IBaseLinkableComponent lc = gwModel;
      Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Created);

      List<IArgument> gwArguments = CreateTestArguments();

      gwModel.Arguments.ApplyArguments(gwArguments);
      gwModel.Initialize();
      Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Initialized);
      Assert.AreEqual("GWModelLC", gwModel.Id);

      Assert.AreEqual(3, gwModel.Inputs.Count);
      Assert.AreEqual(3, gwModel.Outputs.Count);

      {
        // Check the ground water level output
        ITimeSpaceOutput gwLevel = lc.Outputs[2] as ITimeSpaceOutput;
        Assert.IsNotNull(gwLevel);
        Assert.AreEqual("Grid.gwLevel", gwLevel.Id);

        // Check quantity
        IQuantity quantity = gwLevel.ValueDefinition as IQuantity;
        Assert.IsNotNull(quantity);
        Assert.AreEqual("Ground water level", quantity.Caption);
        Assert.AreEqual("gw level", quantity.Unit.Caption);
        Assert.AreEqual(1, quantity.Unit.Dimension.GetPower(DimensionBase.Length));
        Assert.AreEqual(0, quantity.Unit.Dimension.GetPower(DimensionBase.Time));
        Assert.AreEqual(1, quantity.Unit.ConversionFactorToSI);

        // Check element set
        IElementSet gridSet = gwLevel.ElementSet();
        CheckRegularGridElmtSet(gridSet);

      }

      {
        // Check the ground water inflow input
        ITimeSpaceInput gwInput = lc.Inputs[2] as ITimeSpaceInput;
        Assert.IsNotNull(gwInput);
        Assert.AreEqual("Grid.Inflow", gwInput.Id);

        // Check quantity
        IQuantity quantity = gwInput.ValueDefinition as IQuantity;
        Assert.IsNotNull(quantity);
        Assert.AreEqual("Inflow", quantity.Caption);
        Assert.AreEqual("Discharge", quantity.Unit.Caption);
        Assert.AreEqual(3, quantity.Unit.Dimension.GetPower(DimensionBase.Length));
        Assert.AreEqual(-1, quantity.Unit.Dimension.GetPower(DimensionBase.Time));
        Assert.AreEqual(0.001, quantity.Unit.ConversionFactorToSI);

        // Check element set
        IElementSet gridSet = gwInput.ElementSet();
        CheckRegularGridElmtSet(gridSet);
      }

    }
예제 #4
0
        public static ITimeSpaceAdaptedOutput CreateAdaptedOutputMethod(IIdentifiable adaptedOutputId, IBaseOutput adaptee, IBaseInput target)
        {
            IElementSet     elmtSet  = null;
            ITimeSpaceInput tsTarget = target as ITimeSpaceInput;

            if (tsTarget != null && tsTarget.SpatialDefinition is IElementSet)
            {
                elmtSet = tsTarget.ElementSet();
            }

            return(CreateAdaptedOutputMethod(adaptedOutputId, adaptee, elmtSet));
        }
예제 #5
0
        public static IIdentifiable[] GetAvailableMethods(IBaseOutput adaptee, IBaseInput target)
        {
            ITimeSpaceOutput tsadaptee = adaptee as ITimeSpaceOutput;

            // Only works with timespace output as adaptee, and element set as spatial definition
            if (tsadaptee == null || !(tsadaptee.SpatialDefinition is IElementSet))
            {
                return(new IIdentifiable[0]);
            }

            IList <IIdentifiable> methods = new List <IIdentifiable>();

            GetAvailableOperationMethods(ref methods, tsadaptee.ElementSet().ElementType);

            // Check if the target is there and is a timespace input
            ITimeSpaceInput tstarget = target as ITimeSpaceInput;

            if (target == null || tstarget == null || !(tstarget.SpatialDefinition is IElementSet))
            {
                return(((List <IIdentifiable>)methods).ToArray());
            }

            GetAvailableMappingMethods(ref methods, tsadaptee.ElementSet().ElementType, tstarget.ElementSet().ElementType);

            return(((List <IIdentifiable>)methods).ToArray());
        }
예제 #6
0
        public IIdentifiable[] GetAvailableAdaptedOutputIds(IBaseOutput adaptee, IBaseInput target)
        {
            ITimeSpaceOutput tsadaptee = adaptee as ITimeSpaceOutput;
            ITimeSpaceInput  tstarget  = target as ITimeSpaceInput;

            // This inly works with time space items, and target can not be null
            if (tsadaptee == null || tstarget == null)
            {
                return(new IIdentifiable[0]);
            }

            // This only works with element sets
            if (!(tsadaptee.SpatialDefinition is IElementSet && tstarget.SpatialDefinition is IElementSet))
            {
                return(new IIdentifiable[0]);
            }

            return(ElementMapper.GetAvailableMethods(tsadaptee.ElementSet().ElementType, tstarget.ElementSet().ElementType));
        }
예제 #7
0
        public void CouplingGwRiver2()
        {
            /// runNumber 0: Using MultiInput
            /// runNumber 1: Using MultiInputAdaptor
            /// runNumber 2: Using MultiInputAdaptorFactory

            for (int runNumber = 0; runNumber < 3; runNumber++)
            {
                Console.Out.WriteLine("runNumber: " + runNumber);

                // Create trigger inputs
                Input queryDischargeItem = CreateDischargeInput();
                Input queryVolume        = CreateVolumeInput();

                // Create models
                LinkableEngine      riverModel  = CreateRiverModel();
                LinkableEngine      riverModel2 = CreateRiverModel();
                ITimeSpaceComponent gwModel     = CreateGwModel();

                // Add arguments and initialize
                IDictionary <string, IArgument> gwArgs = gwModel.Arguments.Dictionary();
                // Increasing model grid size (otherwise GW model runs full too fast)
                gwArgs.UpdateValue("dx", 50.0);
                gwArgs.UpdateValue("dy", 50.0);
                gwArgs.UpdateValue("x0", 0.0);
                gwArgs.UpdateValue("y0", 200.0);
                gwArgs.UpdateValue("XCount", 24);
                gwArgs.UpdateValue("ycount", 16);
                if (runNumber == 0)
                {
                    gwArgs.UpdateValue("UseMultiInput", true);
                }
                gwModel.Initialize();
                int gwGridSize = 24 * 16;

                IDictionary <string, IArgument> riverArgs = riverModel.Arguments.Dictionary();
                // Increasing model grid size (otherwise GW model runs full too fast)
                riverArgs.UpdateValue("xyscale", 100.0);
                riverModel.Initialize();

                IDictionary <string, IArgument> river2Args = riverModel2.Arguments.Dictionary();
                // Increasing model grid size (otherwise GW model runs full too fast)
                river2Args.UpdateValue("xyscale", 100.0);
                // Move river2 sligthly away from river1
                river2Args.UpdateValue("xoffset", -220.0);
                river2Args.UpdateValue("yoffset", 180.0);
                riverModel2.Initialize();

                // Connect triggering inputs
                ITimeSpaceOutput flowOnBranch  = UTHelper.FindOutputItem(riverModel, "Branch:2:Flow");
                TimeInterpolator flowOnBranch2 = new TimeInterpolator(flowOnBranch);
                flowOnBranch.AddAdaptedOutput(flowOnBranch2);
                flowOnBranch2.AddConsumer(queryDischargeItem);

                ITimeSpaceOutput storageInGw  = UTHelper.FindOutputItem(gwModel, "Grid.Storage");
                TimeInterpolator storageInGw2 = new TimeInterpolator(storageInGw);
                storageInGw.AddAdaptedOutput(storageInGw2);
                storageInGw2.AddConsumer(queryVolume);

                //========== Couple leakage items ==========
                ITimeSpaceInput gwInflowInput = UTHelper.FindInputItem(gwModel, "Grid.Inflow");


                //========== IBaseMultiInput linking ==========
                if (runNumber == 0)
                {
                    /// Example of adding up two outputs into one input, by the use of
                    /// an IBaseMultiInput implementation

                    Assert.IsTrue(gwInflowInput is IBaseMultiInput);
                    Assert.IsTrue(gwInflowInput is ITimeSpaceMultiInput);

                    // put leakage from river1 into ground water model
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel, "WholeRiver:Leakage");

                        // Two adaptors are added: Time buffer and line-to-grid adaptor
                        // they can be added in any order (though time buffer first will use less memory)

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: No special action
                        riverLeakageOutputGrid.AddConsumer(gwInflowInput);
                    }

                    // put leakage from river2 into ground water model
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel2, "WholeRiver:Leakage");

                        // Two adaptors are added: Time buffer and line-to-grid adaptor
                        // they can be added in any order (though time buffer first will use less memory)

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: No special action
                        riverLeakageOutputGrid.AddConsumer(gwInflowInput);
                    }
                }

                //========== MultiInputAdaptor linking ==========
                if (runNumber == 1)
                {
                    /// Example of adding up two outputs into one input, by the use of
                    /// a MultiInputAdaptor class

                    // Note !!!: Creating a MultiInputAdaptor
                    MultiInputAdaptor sourceAdder = new MultiInputAdaptor("SomeId")
                    {
                        SpatialDefinition = gwInflowInput.SpatialDefinition
                    };

                    // put leakage from river1 into ground water model
                    // Two adaptors are added: Time buffer and line-to-grid adaptor
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel, "WholeRiver:Leakage");

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: Adding to the list of adaptees
                        sourceAdder.Adaptees.Add(riverLeakageOutputGrid);
                        riverLeakageOutputGrid.AddAdaptedOutput(sourceAdder);
                    }

                    // put leakage from river2 into ground water model
                    // Two adaptors are added: Time buffer and line-to-grid adaptor
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel2, "WholeRiver:Leakage");

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: Adding to the list of adaptees
                        sourceAdder.Adaptees.Add(riverLeakageOutputGrid);
                        riverLeakageOutputGrid.AddAdaptedOutput(sourceAdder);
                    }

                    // Note !!!: Connect the gwInflowInput and the multiInputAdaptor
                    sourceAdder.AddConsumer(gwInflowInput);
                }

                //========== MultiInputAdaptorFactory linking ==========
                if (runNumber == 2)
                {
                    /// Example of adding up two outputs into one input, by the use of
                    /// an MultiInputAdaptorFactory implementation

                    var factory = new MultiInputAdaptorFactory(gwModel);

                    // put leakage from river1 into ground water model
                    // Two adaptors are added: Time buffer and line-to-grid adaptor
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel, "WholeRiver:Leakage");

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: Creating a new AdaptedOutput and adding it
                        IIdentifiable[]    identifiables = factory.GetAvailableAdaptedOutputIds(riverLeakageOutputGrid, gwInflowInput);
                        IBaseAdaptedOutput myOutput      = factory.CreateAdaptedOutput(identifiables[0], riverLeakageOutputGrid, gwInflowInput);

                        myOutput.AddConsumer(gwInflowInput);
                    }

                    // put leakage from river2 into ground water model
                    // Two adaptors are added: Time buffer and line-to-grid adaptor
                    {
                        ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel2, "WholeRiver:Leakage");

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Note !!!: Creating a new AdaptedOutput and adding it
                        IIdentifiable[]    identifiables = factory.GetAvailableAdaptedOutputIds(riverLeakageOutputGrid, gwInflowInput);
                        IBaseAdaptedOutput myOutput      = factory.CreateAdaptedOutput(identifiables[0], riverLeakageOutputGrid, gwInflowInput);

                        myOutput.AddConsumer(gwInflowInput);
                    }
                }


                //========== Run ==========

                // Validate
                riverModel.Validate();
                Assert.IsTrue(riverModel.Status == LinkableComponentStatus.Valid);
                riverModel2.Validate();
                Assert.IsTrue(riverModel2.Status == LinkableComponentStatus.Valid);
                gwModel.Validate();
                Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Valid);

                // Prepare
                riverModel.Prepare();
                Assert.IsTrue(riverModel.Status == LinkableComponentStatus.Updated);
                riverModel2.Prepare();
                Assert.IsTrue(riverModel2.Status == LinkableComponentStatus.Updated);
                gwModel.Prepare();
                Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Updated);


                // specify query times
                double triggerTime0 = riverModel.CurrentTime.StampAsModifiedJulianDay;
                double triggerTime1 = triggerTime0 + 1;
                double triggerTime2 = triggerTime0 + 2;
                double triggerTime3 = triggerTime0 + 12.1;
                double triggerTime4 = triggerTime0 + 16.7;

                /// Properties of the river, without gw-level input
                /// Inflow into each node from rainfall runoff is 10 L/s
                /// Inflow to node 1: 10        L/s - leaking  5   L/s on branch 1
                /// Inflow to node 2: 10 +    5 L/s - leaking 15/2 L/s on branch 2
                /// Inflow to node 3: 10 + 15/2 L/s - leaking 35/4 L/s on branch 3
                /// Total leakage 5+15/2+35/4 = (20+30+35)/4 = 85/4 L/s
                ///
                /// Number of seconds in a day: 60*60*24 = 86400

                // check initial values
                Assert.AreEqual(1, ValueSet.GetElementCount(flowOnBranch.Values), "#values for " + flowOnBranch.Id);
                Assert.AreEqual(7.0, (double)flowOnBranch.Values.GetValue(0, 0), "Value[0] as property");

                Assert.AreEqual(gwGridSize, ValueSet.GetElementCount(storageInGw.Values), "#values for " + storageInGw.Id);
                Assert.AreEqual(0, SumTimeStep(storageInGw.Values, 0));

                // get values for specified query times, 1 days
                // Totally leaking: 86400 * 85/4 = 1.836e6
                // For the bi-directional coupling:
                // the entire first day the river uses extrapolated values from the
                // gwModel, which gives a gwLevel of -10, hence same value as for the uni-directional
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime1);
                ITimeSpaceValueSet valuesV = storageInGw2.GetValues(queryDischargeItem);
                ITimeSpaceValueSet valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                Assert.AreEqual(2 * 1.836e6, SumTimeStep(valuesV, 0), 1e-4);

                // Print out, to load in a plotting program for verification
                StringBuilder b = new StringBuilder();

                IList valV   = valuesV.GetElementValuesForTime(0);
                int   ivalvV = 0;
                for (int i = 0; i < 16; i++)
                {
                    for (int j = 0; j < 24; j++)
                    {
                        b.Append(((double)valV[ivalvV++]).ToString(NumberFormatInfo.InvariantInfo));
                        b.Append(" ");
                    }
                    b.AppendLine();
                }
                //Console.Out.WriteLine(b.ToString());

                // get values for specified query times, 2 days
                // 2 * 86400 * 85/4 = 3.672e6
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime2);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                Assert.AreEqual(2 * 3.672e6, SumTimeStep(valuesV, 0), 1e-4);

                // get values for specified query times, 12.1 days
                // 12.1 * 86400 * 85/4 = 2.22156e7
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime3);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                Assert.AreEqual(2 * 2.22156e7, SumTimeStep(valuesV, 0), 1e-4);

                // get values for specified query times, 16.7 days
                // 16.7 * 86400 * 85/4 = 3.06612e7
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime4);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                Assert.AreEqual(2 * 3.06612e7, SumTimeStep(valuesV, 0), 1e-4);
            }
        }
예제 #8
0
        public void CouplingGwRiver()
        {
            /// bit 1: Decides whether the timeInterpolator or grid-to-line adaptor comes first
            /// bit 2: when true, using a 16x16 gw grid (instead of 2x2)
            /// bit 3: when true, bi-directinal: adds a link from gwModel to river, with the gw-level

            for (int runNumber = 0; runNumber < 8; runNumber++)
            {
                //if (runNumber != 3)
                //  continue;

                Console.Out.WriteLine("runNumber: " + runNumber);

                // Create trigger inputs
                Input queryDischargeItem = CreateDischargeInput();
                Input queryVolume        = CreateVolumeInput();

                // Create models
                LinkableEngine      riverModel = CreateRiverModel();
                ITimeSpaceComponent gwModel    = CreateGwModel();

                // Add arguments and initialize
                IDictionary <string, IArgument> gwArgs = gwModel.Arguments.Dictionary();
                // Increasing model grid size (otherwise GW model runs full too fast)
                gwArgs.UpdateValue("dx", 400.0);
                gwArgs.UpdateValue("dy", 400.0);
                gwArgs.UpdateValue("x0", 200.0);
                gwArgs.UpdateValue("y0", 200.0);
                int gwGridSize = 2 * 2;
                if ((runNumber & 2) == 2) // set 16 x 16 grid
                {
                    gwArgs.UpdateValue("dx", 50.0);
                    gwArgs.UpdateValue("dy", 50.0);
                    gwArgs.UpdateValue("XCount", 16);
                    gwArgs.UpdateValue("ycount", 16);
                    gwGridSize = 16 * 16;
                }
                gwModel.Initialize();

                IDictionary <string, IArgument> riverArgs = riverModel.Arguments.Dictionary();
                // Increasing model grid size (otherwise GW model runs full too fast)
                riverArgs.UpdateValue("xyscale", 100.0);
                riverModel.Initialize();

                // Connect triggering inputs
                ITimeSpaceOutput flowOnBranch  = UTHelper.FindOutputItem(riverModel, "Branch:2:Flow");
                TimeInterpolator flowOnBranch2 = new TimeInterpolator(flowOnBranch);
                flowOnBranch.AddAdaptedOutput(flowOnBranch2);
                flowOnBranch2.AddConsumer(queryDischargeItem);

                ITimeSpaceOutput storageInGw  = UTHelper.FindOutputItem(gwModel, "Grid.Storage");
                TimeInterpolator storageInGw2 = new TimeInterpolator(storageInGw);
                storageInGw.AddAdaptedOutput(storageInGw2);
                storageInGw2.AddConsumer(queryVolume);

                //========== Couple leakage items ==========
                // put leakage from river into ground water model
                {
                    ITimeSpaceOutput riverLeakageOutput = UTHelper.FindOutputItem(riverModel, "WholeRiver:Leakage");
                    ITimeSpaceInput  gwInflowInput      = UTHelper.FindInputItem(gwModel, "Grid.Inflow");

                    // Two adaptors are added: Time buffer and line-to-grid adaptor
                    // they can be added in any order (though time buffer first will use less memory)
                    if ((runNumber & 1) == 1)
                    {
                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutput);
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput2,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput2.AddAdaptedOutput(riverLeakageOutputGrid);

                        riverLeakageOutputGrid.AddConsumer(gwInflowInput);
                    }
                    else
                    {
                        // Element mapper from polyline to polygon, weighted sum version
                        ElementMapperAdaptedOutput riverLeakageOutputGrid =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper501"), riverLeakageOutput,
                                                           gwInflowInput.ElementSet());
                        riverLeakageOutput.AddAdaptedOutput(riverLeakageOutputGrid);

                        // Time interpolator
                        TimeInterpolator riverLeakageOutput2 = new TimeInterpolator(riverLeakageOutputGrid);
                        riverLeakageOutputGrid.AddAdaptedOutput(riverLeakageOutput2);

                        riverLeakageOutput2.AddConsumer(gwInflowInput);
                    }
                }
                //========== Couple ground water level items ==========

                if ((runNumber & 4) == 4)
                {
                    // put ground water level from ground water model into river
                    ITimeSpaceInput  riverGwleveInput = UTHelper.FindInputItem(riverModel, "WholeRiver:GroundWaterLevel");
                    ITimeSpaceOutput gwLevelOutput    = UTHelper.FindOutputItem(gwModel, "Grid.gwLevel");

                    // Two adaptors are added: Time buffer and grid-to-line adaptor
                    // they can be added in any order (though time buffer last will use less memory)
                    if ((runNumber & 1) == 1)
                    {
                        // Time interpolator
                        var gwLevelOutput2 = new TimeExtrapolator(gwLevelOutput);
                        gwLevelOutput.AddAdaptedOutput(gwLevelOutput2);

                        // Element mapper from polyline to polygon, weighted sum version
                        var gwLevelOutputLine =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper700"), gwLevelOutput2,
                                                           riverGwleveInput.ElementSet());
                        gwLevelOutput2.AddAdaptedOutput(gwLevelOutputLine);

                        gwLevelOutputLine.AddConsumer(riverGwleveInput);
                    }
                    else
                    {
                        // Element mapper from polyline to polygon, weighted sum version
                        var gwLevelOutputLine =
                            new ElementMapperAdaptedOutput(new Identifier("ElementMapper700"), gwLevelOutput,
                                                           riverGwleveInput.ElementSet());
                        gwLevelOutput.AddAdaptedOutput(gwLevelOutputLine);

                        // Time interpolator
                        var gwLevelOutput2 = new TimeExtrapolator(gwLevelOutputLine);
                        gwLevelOutputLine.AddAdaptedOutput(gwLevelOutput2);

                        gwLevelOutput2.AddConsumer(riverGwleveInput);
                    }
                }

                //========== Run ==========

                // Validate
                riverModel.Validate();
                Assert.IsTrue(riverModel.Status == LinkableComponentStatus.Valid);
                gwModel.Validate();
                Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Valid);

                // Prepare
                riverModel.Prepare();
                Assert.IsTrue(riverModel.Status == LinkableComponentStatus.Updated);
                gwModel.Prepare();
                Assert.IsTrue(gwModel.Status == LinkableComponentStatus.Updated);


                // specify query times
                double triggerTime0 = riverModel.CurrentTime.StampAsModifiedJulianDay;
                double triggerTime1 = triggerTime0 + 1;
                double triggerTime2 = triggerTime0 + 2;
                double triggerTime3 = triggerTime0 + 12.1;
                double triggerTime4 = triggerTime0 + 16.7;

                /// Properties of the river, without gw-level input
                /// Inflow into each node from rainfall runoff is 10 L/s
                /// Inflow to node 1: 10        L/s - leaking  5   L/s on branch 1
                /// Inflow to node 2: 10 +    5 L/s - leaking 15/2 L/s on branch 2
                /// Inflow to node 3: 10 + 15/2 L/s - leaking 35/4 L/s on branch 3
                /// Total leakage 5+15/2+35/4 = (20+30+35)/4 = 85/4 L/s
                ///
                /// Number of seconds in a day: 60*60*24 = 86400

                // check initial values
                Assert.AreEqual(1, ValueSet.GetElementCount(flowOnBranch.Values), "#values for " + flowOnBranch.Id);
                Assert.AreEqual(7.0, (double)flowOnBranch.Values.GetValue(0, 0), "Value[0] as property");

                Assert.AreEqual(gwGridSize, ValueSet.GetElementCount(storageInGw.Values), "#values for " + storageInGw.Id);
                Assert.AreEqual(0, SumTimeStep(storageInGw.Values, 0));

                // get values for specified query times, 1 days
                // Totally leaking: 86400 * 85/4 = 1.836e6
                // For the bi-directional coupling:
                // the entire first day the river uses extrapolated values from the
                // gwModel, which gives a gwLevel of -10, hence same value as for the uni-directional
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime1);
                ITimeSpaceValueSet valuesV = storageInGw2.GetValues(queryDischargeItem);
                ITimeSpaceValueSet valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                Assert.AreEqual(1.836e6, SumTimeStep(valuesV, 0), 1e-4);

                // Print out, to load in a plotting program for verification
                //StringBuilder b = new StringBuilder();
                //foreach (double val in valuesV.GetElementValuesForTime(0))
                //  b.AppendLine(val.ToString(NumberFormatInfo.InvariantInfo));
                //Console.Out.WriteLine(b.ToString());

                // get values for specified query times, 2 days
                // 2 * 86400 * 85/4 = 3.672e6
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime2);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                if ((runNumber & 4) != 4) // unidirectional
                {
                    Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                    Assert.AreEqual(3.672e6, SumTimeStep(valuesV, 0), 1e-4);
                }
                else if ((runNumber & 2) != 2) // bi-directional 2x2 grid
                {
                    Assert.AreEqual(8.843648, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(3.66390879366e6, SumTimeStep(valuesV, 0), 1e-4);
                }
                else                    // bi-directional 16x16 grid
                {
                    Assert.AreEqual(9.65307, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(3.59397465219e6, SumTimeStep(valuesV, 0), 1e-4);
                }

                // get values for specified query times, 12.1 days
                // 12.1 * 86400 * 85/4 = 2.22156e7
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime3);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                if ((runNumber & 4) != 4) // unidirectional
                {
                    Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                    Assert.AreEqual(2.22156e7, SumTimeStep(valuesV, 0), 1e-4);
                }
                else if ((runNumber & 2) != 2) // bi-directional 2x2 grid
                {
                    Assert.AreEqual(9.87828, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(2.16704019338e7, SumTimeStep(valuesV, 0), 1e-4);
                }
                else                    // bi-directional 16x16 grid
                {
                    Assert.AreEqual(18.546999, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(1.722400002557e7, SumTimeStep(valuesV, 0), 1e-4);
                }

                // get values for specified query times, 16.7 days
                // 16.7 * 86400 * 85/4 = 3.06612e7
                queryDischargeItem.TimeSet.SetSingleTimeStamp(triggerTime4);
                valuesV = storageInGw2.GetValues(queryDischargeItem);
                valuesQ = flowOnBranch2.GetValues(queryDischargeItem);
                if ((runNumber & 4) != 4) // unidirectional
                {
                    Assert.AreEqual(35.0 / 4.0, (double)valuesQ.GetValue(0, 0));
                    Assert.AreEqual(3.06612e7, SumTimeStep(valuesV, 0), 1e-4);
                }
                else if ((runNumber & 2) != 2) // bi-directional 2x2 grid
                {
                    Assert.AreEqual(10.255535, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(2.9595872035072e7, SumTimeStep(valuesV, 0), 1e-4);
                }
                else                    // bi-directional 16x16 grid
                {
                    Assert.AreEqual(20.98699, (double)valuesQ.GetValue(0, 0), 1e-4);
                    Assert.AreEqual(2.12991179998e7, SumTimeStep(valuesV, 0), 1e-4);
                }
            }
        }
        public void Inputs()
        {
            Assert.AreEqual(8, simpleRiver.Inputs.Count);

            ITimeSpaceInput input = (ITimeSpaceInput)simpleRiver.Inputs[0];

            Assert.AreEqual("Branch:0:InFlow", input.Id);
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Branch:0", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.PolyLine, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(0));
            Assert.AreEqual(1000.0, input.ElementSet().GetVertexXCoordinate(0, 0));
            Assert.AreEqual(7000.0, input.ElementSet().GetVertexXCoordinate(0, 1));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexYCoordinate(0, 0));
            Assert.AreEqual(6000.0, input.ElementSet().GetVertexYCoordinate(0, 1));

            input = (ITimeSpaceInput)simpleRiver.Inputs[1];
            Assert.AreEqual("Branch:1:InFlow", input.Id);
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Branch:1", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.PolyLine, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(0));
            Assert.AreEqual(7000.0, input.ElementSet().GetVertexXCoordinate(0, 0));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexXCoordinate(0, 1));
            Assert.AreEqual(6000.0, input.ElementSet().GetVertexYCoordinate(0, 0));
            Assert.AreEqual(3000.0, input.ElementSet().GetVertexYCoordinate(0, 1));

            input = (ITimeSpaceInput)simpleRiver.Inputs[2];
            Assert.AreEqual("Branch:2:InFlow", input.Id);
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Branch:2", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.PolyLine, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(0));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexXCoordinate(0, 0));
            Assert.AreEqual(14000.0, input.ElementSet().GetVertexXCoordinate(0, 1));
            Assert.AreEqual(3000.0, input.ElementSet().GetVertexYCoordinate(0, 0));
            Assert.AreEqual(1000.0, input.ElementSet().GetVertexYCoordinate(0, 1));

            input = (ITimeSpaceInput)simpleRiver.Inputs[3];
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Node:0", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.IdBased, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);

            input = (ITimeSpaceInput)simpleRiver.Inputs[4];
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Node:1", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.IdBased, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);

            input = (ITimeSpaceInput)simpleRiver.Inputs[5];
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Node:2", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.IdBased, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);

            input = (ITimeSpaceInput)simpleRiver.Inputs[6];
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("Node:3", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.IdBased, input.ElementSet().ElementType);
            Assert.AreEqual(1, input.ElementSet().ElementCount);

            input = (ITimeSpaceInput)simpleRiver.Inputs[7];
            Assert.AreEqual("InFlow", input.ValueDefinition.Caption);
            Assert.AreEqual("m3/sec", ((IQuantity)input.ValueDefinition).Unit.Caption);
            Assert.AreEqual(typeof(double).ToString(), input.ValueDefinition.ValueType.ToString());
            Assert.AreEqual("AllBranches", input.ElementSet().Caption);
            Assert.AreEqual(ElementType.PolyLine, input.ElementSet().ElementType);
            Assert.AreEqual(3, input.ElementSet().ElementCount);
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(0));
            Assert.AreEqual(1000.0, input.ElementSet().GetVertexXCoordinate(0, 0));
            Assert.AreEqual(7000.0, input.ElementSet().GetVertexXCoordinate(0, 1));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexYCoordinate(0, 0));
            Assert.AreEqual(6000.0, input.ElementSet().GetVertexYCoordinate(0, 1));
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(1));
            Assert.AreEqual(7000.0, input.ElementSet().GetVertexXCoordinate(1, 0));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexXCoordinate(1, 1));
            Assert.AreEqual(6000.0, input.ElementSet().GetVertexYCoordinate(1, 0));
            Assert.AreEqual(3000.0, input.ElementSet().GetVertexYCoordinate(1, 1));
            Assert.AreEqual(2, input.ElementSet().GetVertexCount(2));
            Assert.AreEqual(9000.0, input.ElementSet().GetVertexXCoordinate(2, 0));
            Assert.AreEqual(14000.0, input.ElementSet().GetVertexXCoordinate(2, 1));
            Assert.AreEqual(3000.0, input.ElementSet().GetVertexYCoordinate(2, 0));
            Assert.AreEqual(1000.0, input.ElementSet().GetVertexYCoordinate(2, 1));
        }