This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Running Hammer with WaterObjects.NET

Hello There,

I am using WaterObjects.NET to automate Hammer for sensitivity and calibration purpose. I can open the project, get/set pipe parameters, but get stuck in running Hammer engine.

My questions are:
1 Is it possible to run epanet (as show in RunEpaNet(...)) with only Hammer license? We only have Hammer license.
2 I have to call the project.Save(property); to save my edits, eg pipe diameter and wave speed, is my way of generating ProjectProperty correct?
3 When i come toline hammerEngine.Run(domainDataSet.ScenarioManager.Elements()); I always get error NullReference. After I call SetUnitReference(project.NumericPresentationManager), the NullReference error was gone, but get another error msg (please note I can see the scenarios running window): "The process cannot access the file 'C:\\Users\\hshen\\AppData\\Local\\Temp\\Bentley\\HAMMER\\test PRV.wtg.6.$$$_1_1.hof' because it is being used by another process."

Thanks for the help,

Hailiang

Here are my codes:

private const string VERSION = "08.11.05.61";

        public Form1()
        {
            InitializeComponent();
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            //RunEpaNet();

            #region Open a wtg project

            IProjectApplicationModel aprojectApplicationModel = (IProjectApplicationModel)new Haestad.Toronto.Application.TorontoApplicationModel(LicensePlatformType.Standalone, VERSION, new string[] { });

            Haestad.Toronto.Application.TorontoParentFormModel parentFormModel = new Haestad.Toronto.Application.TorontoParentFormModel(aprojectApplicationModel);
            Haestad.Toronto.Application.TorontoParentFormUIModel parentFormUIModel = new Haestad.Toronto.Application.TorontoParentFormUIModel(parentFormModel);
            parentFormUIModel.InitializeOnStartUp();

            // opem hammer project
            bool sucess = parentFormUIModel.OpenFileWithSpecifiedExtension(txtInput.Text, false);
            if (sucess)
            {
                Haestad.Toronto.Application.TorontoProject project = parentFormModel.CurrentProject as Haestad.Toronto.Application.TorontoProject;
                Haestad.HAMMER.TransientResultsViewer.Domain.TransientResultsProjectContext projectContext = new Haestad.HAMMER.TransientResultsViewer.Domain.TransientResultsProjectContext(
                    project.ProjectManager, project.TransientResultsViewerProfileElementManager, project.TransientResultsViewerTimeHistoryElementManager, project.TransientResultsViewerNodeHistoryElementManager);

                Haestad.Framework.Application.ProjectProperties property = Haestad.Framework.Application.ProjectProperties.DefaultNominalProjectPath(txtInput.Text);
               
                Haestad.Domain.ModelingObjects.Water.IdahoDomainDataSet domainDataSet = project.IdahoDomainDataSet;
                this.RunScenarios(project);

                project.Save(property);
            }

            #endregion
        }

        private void RunEpaNet(string inp = @"C:\Program Files (x86)\EPANET2\Examples\Net3.inp")
        {
            // using Haestad.Calculations.Pressure.NumericalEngine.EngineAdapter will not work for the license limit.
            Haestad.Calculations.Pressure.NumericalEngine.EngineAdapter adapter = new Haestad.Calculations.Pressure.NumericalEngine.EngineAdapter();
            adapter.SetLicensingInfo(this.RetrieveLicense());
            adapter.Open(inp, "", "");
            adapter.OpenHydraulicEngine();
            adapter.InitializeHydraulicEngine(true);
        }

        private void RunScenarios(Haestad.Toronto.Application.TorontoProject project)
        {
            try
            {
                // run all scenarios at first
                Haestad.Domain.ModelingObjects.Water.IdahoDomainDataSet domainDataSet = project.IdahoDomainDataSet;
                Haestad.Calculations.Hammer.Domain.HammerNumericalEngine hammerEngine =
                    (Haestad.Calculations.Hammer.Domain.HammerNumericalEngine)domainDataSet.NumericalEngine(Haestad.Domain.StandardCalculationOptionFieldName.HammerNumericalEngine);

                //Haestad.Calculations.Hammer.Domain.HammerNumericalEngine hammerEngine = new Haestad.Calculations.Hammer.Domain.HammerNumericalEngine(domainDataSet, Haestad.Domain.StandardCalculationOptionFieldName.HammerNumericalEngine);

                hammerEngine.SetUnitReference(project.NumericPresentationManager);
                hammerEngine.SetLicensingInfo(this.RetrieveLicense());
                hammerEngine.ComputeInitialConditionsSetting = Haestad.Calculations.Hammer.Domain.ComputeInitialConditionSetting.AlwaysCompute;

                Haestad.Domain.ModelingElementCollection modelingElementCollection = new Haestad.Domain.ModelingElementCollection();
                modelingElementCollection.Add(domainDataSet.ScenarioManager.Element(1));
                if (!hammerEngine.IsRunning(1))
                {
                    hammerEngine.Run(modelingElementCollection);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private Haestad.Support.Support.HmIDCollection GetScenarioIds(Haestad.Domain.ModelingObjects.Water.IdahoDomainDataSet domainDataSet)
        {
            return domainDataSet.ScenarioManager.ElementIDs();
        }

        private Haestad.LicensingFacade.License RetrieveLicense()
        {
            // is relevant to an engine, not specific to a project. A project may require multiple licenses, eg hammer requires both epanet and hammer
            ProductRelease release = new ProductRelease(ProductId.Bentley_HAMMER, VERSION);
            Haestad.LicensingFacade.License license = Haestad.LicensingFacade.License.Default(release);
            license.StartDesktop(true);
            return license;
        }

  • Hello Shenh,

    For your first question, as far as I know, there should not be an issue running the steady state/eps solver with only a HAMMER license. This is done in HAMMER itself to establish initial conditions for a transient analysis. For the other problem with running the solver, check out the below forum threads, which are also related to running the HAMMER solver with Waterobjects. Let us know if this doesn't help and we'll get some input from the development team.

     

    https://communities.bentley.com/products/hydraulics___hydrology/f/5925/t/100796

    https://communities.bentley.com/products/hydraulics___hydrology/f/5925/t/100798


    Regards,

    Jesse Dringoli
    Technical Support Manager, OpenFlows
    Bentley Communities Site Administrator
    Bentley Systems, Inc.

  • Thanks Jesse, I did go through that two posts. The second one is very helpful, as you can see I was using that as a reference for my code to open a .wtg file. I do not think my questions were answered.

    When I tried RunEpaNet(...) (the line adapter.Open(inp, "", "");), it gave me an error "Specified method is not supported". I am directing my exe file to the Bentley installation folder, so I think all required dlls should be located. That is why I thought it was a license issue.
  • Hi Shenh,

    You are very close to getting things to work.  But it looks like you went the hard way.

    First, there is no need to use the EpaNet engine (EngineAdapter) directly.  That is what WO.net does for you.

    Second, it would be easier to just show you a snippet of code I put together instead of trying to refactor your code.  Many of the issues you were running into (like calling SetUnitReference) is taken care of for you instead of having to do it yourself.  That's what I like to call the "Power of the Framework".

    Since you are using the Application Framework portion of WaterObjects.Net, getting this to work is actually quite simple.

    private void OpenBatchRunAndSaveHAMMERProject()

    {

    IProjectApplicationModel aprojectApplicationModel = new TorontoApplicationModel(LicensePlatformType.Standalone,

    Assembly.GetExecutingAssembly().GetName().Version.ToString(), new object[] { });

    TorontoParentFormModel aparentFormModel = new TorontoParentFormModel(aprojectApplicationModel);

    TorontoParentFormUIModel aparentFormUIModel = new TorontoParentFormUIModel(aparentFormModel);

    aparentFormUIModel.InitializeOnStartUp();

    if (aparentFormUIModel.OpenProject(@"F:\Documents\Bentley\HAMMER\Samples\Sample1.wtg"))

    {

    IdahoBatchRunEditorFormModel batchRunFormModel = new IdahoBatchRunEditorFormModel(aprojectApplicationModel, BatchRunType.All, aparentFormUIModel.CurrentGraphicalProject.DomainDataSet.ScenarioManager.ActiveScenarioID);

    batchRunFormModel.BatchRun();

    if (batchRunFormModel.BatchRunSuccessful)

    {

    //Get results you need.

    }

    //Saves any changes you made and keeps a copy of the result files (copies them to the project location by default)

    aparentFormUIModel.SaveCurrentProject();

    }

    //Make sure this is called to clean everything up.

    aparentFormUIModel.CloseParentFormUIModel();

    }

    Let me explain the code to you so you can understand what is going on.

    1.  The first line creates the ApplicationModel.  The instance here should live for the lifetime of the your session (the "session" defined as hitting F5 from Visual Studio or running the exe directly until the application is closed).  I recommend that this instance be instantiated in your Form's constructor and live as a private field in the form.

    2.  The second line is the ParentFormModel.  This is the class that goes with the ParentForm.  In this case it would normally be the TorontoParentForm but I don't need to create one in my snippet.  It allows for access to common methods and properties that you may need.  In this snippet it is only needed in order to create an instance of the ParentFormUIModel (see #3).  This should also be a private field in your Form and instantiated in the constructor.

    3.  The ParentFormUIModel is the "master" form model.  It is primarily used to execute "commands".  Since we don't want to use the UI per-se in this case, we only use the ParentFormUIModel to open the project.  Calling InitializeOnStartUp() is an important step because it sets up internals that may be required.  This should also be a private field of your form.  Make sure you call CloseParentFormUIModel() in the FormClosing event handler.  This is very important.

    4.  The next line opens the project.  In this case I put in a hard-coded path and filename.  You should probably use the OpenFileDialo class) to prompt the user for the file and then pass in that filename into this method.  Or just replace it with your own hardcoded path and filename.  Whatever you deem necessary.

    5.  I used an "if" statement on the OpenProject call because true will determine a successful open.  Once the project is open I create an instance of the BatchRunFormModel, in this case the Idaho specific implementation (Idaho is the internal "name" for WaterGEMS).  Based on the code you provided I surmised that you wanted to compute all the scenarios in the model.  That is what the Batch Run is for.  In this case it is simply bypassing the UI that you see when you use it from the Scenario Manager.  You may be prompted about needing to compute the initial conditions for x or more scenarios.  Be sure you click Yes or make sure the "Always Compute Initial Conditions" option is set to true for the project (this can be done via HAMMER directly or through code when you've already written).

    6.  Once all the scenarios have been computed, I check the BatchRunSuccessful property of the FormModel.  This is a Boolean where True is a successful run and false is otherwise.  If the calculations are successful then you can access the results via the Framework API.  You already started doing that.

    That should get you going.  Using the Application Framework part of the WO.Net is the recommended approach, especially for when you get to the step of retrieving HAMMER results.  

    Have fun.

    Kris Culin

    Bentley Software

  • Unfortunately Kris, I cannot get it work with the sample.

    Without adding batchRunFormModel.SelectAll(), I have zero scenario for batch run. After I add batchRunFormModel.SelectAll(), I get error message "Object reference not set to an instance of an object", I watch the variable batchRunFormModel, and find its OwerWindow throws NullReference, which may be the source of this error msg. But this OwerWindow property is read only, so it may need to be set somewhere else.

    Also: a) do we still need to set up the license for Hammer somewhere? Since the license is defined in NumericalEngine level and this batch run method does not touch the engine, b) can I just use batchRunFormModel.ExecuteBatchRun to execute the active scenario ONLY? I do not want to run all scenarios. I want something such as: (1) change wave speed for active scenario, (2) run the transient model, (3) extract transient results, (4) repeat steps (1) - (3).

    The reason I want to run EPANET is that I have experience in working with the original EPANET toolkit (eg ENgetlinkvalue etc), and want to find the correspondence in WaterObjects.NET. My sample code will not work. So should I resort to other namespaces?

    Thanks

    Hailiang
  • Hi Shenh,

    I'm surprised it is not working for you. I apologize.

    So, let's see if we can diagnose what is going on.

    For the OwnerWindow to be valid, implement IUserInterface defined in Haestad.Framework.Application on your main form. For the Open(IApplicationContext) and ModelessFormClosing() methods just leave them as no-ops.

    For the OwnerWindow property, return "this" (no quotes) which would be your Form. The interface must be implemented on your Form for this to work.

    The TorontoApplicationModel has a method called SetCOMUserInterface. Once your form implements IUserInterface you can pass it into this method. It will set the proper internals so down the line UserInterface on the ApplicationModel is no longer null.

    Once you do the above you should be able to call SelectAll() and then call BatchRun().

    The license is set automatically for you since you are using the Application Framework API. When you compute, the license is retrieved from the LicenseFeatureSet property of the ApplicationModel. The license will also be released upon proper closure of the application (one of the reasons why calling CloseParentFormUIModel() is so important when your main form closes and the application ends). The license is appropriately applied to both the water and HAMMER engines as needed using the SetLicensingInfo method.

    If you do not want to run all scenarios (that is what I interpreted in your original posting) then there is a simpler way to compute the HAMMER engine.

    //Compute the initial conditions with the Water engine.
    aparentFormUIModel.ExecuteCommand(CommandType.CurrentScenario, scenarioId);

    //Compute the transient analysis with the HAMMER engine
    aparentFormUIModel.ExecuteCommand(CommandType.ComputeHammer, scenarioId);

    The scenarioId value does not have to be the Active Scenario. It can be any valid scenario ID in the model. The code will blow up if this is not a valid scenario id.

    I hope that helps.

    Kris