/// <summary> /// set property /// </summary> /// <param name="propName">property name, defined in CO reference</param> /// <param name="phases">empty means overall, if it's vapor and liquid, will be combined to "vaporliquid", otherwise, just add the names</param> /// <returns></returns> protected void SetPropList(string propName, PropertyBasis basis, IEnumerable <double> value, Phases[] phases = null) { CasterLogger.Debug($"Set property for {propName}: {value}"); double[] temp = value as double[] ?? value.ToArray(); if (phases == null || phases.Length == 0) // overall { CasterLogger.Debug($"Set property {propName} for overall phase"); _capeThermoMaterialObject.SetProp(propName, "Overall", null, "mixture", basis.ToString(), temp); } else if (phases.Length == 1) // one phase { CasterLogger.Debug($"Set property {propName} for {phases[0]}"); _capeThermoMaterialObject.SetProp(propName, phases[0].Value, null, null, basis.ToString(), temp); } else if (phases.Length == 2) // two phases { CasterLogger.Debug($"Set property {propName} for {phases[0]} and {phases[1]}"); string phaseName = null; if (phases.Contains(Phases.Vapor) && phases.Contains(Phases.Liquid)) { phaseName = Phases.Vapor.Value + Phases.Liquid.Value; // vapor in front } else { phaseName = phases[0].Value + phases[1].Value; } _capeThermoMaterialObject.SetProp(propName, phaseName, null, "mixture", basis.ToString(), temp); } else { CasterLogger.Error($"Only support overall, 1 phase or 2 phases"); throw new ArgumentOutOfRangeException($"Only support overall, 1 phase or 2 phases"); } //_alreadyFlashed = false; CasterLogger.Debug($"Set property completed"); }
/// <summary> /// Calculation method for the MixerExample unit operation. /// </summary> /// <remarks> /// A mixer unit operation combined the material flows from two inlet ports into the flow of a single outlet port. /// In order to do this calculation, the mixer unit obtains flow information from each inlet port, /// the adds the flows to obtain the flow of the outlet port. In the case of the mixer below, it is assumed that the /// components are the same in each material object and that the components are listed in the same order. /// After the combined flow is calculated at the values set to the outlet port, along with the /// enthalpy of the stream calculated from an energy balance and the pressure determined from /// the inlet pressures, the outlet stream can be flahsed to determine equilibrium conditions. /// The last task is releasing any duplicate material objects obtained. /// </remarks> /// <example> /// <para>An example of how to calculate a unit operation. This method obtains material objects /// from each of ports using the <see cref = "PortCollection"/> class. The <see cref = "ICapeThermoMaterialObject"/> /// interface is used to obtain the names using the CompIds() method, flows of each of the /// components present in the material object, overall pressure and overall enthalpy are /// obtained using the <see cref = "ICapeThermoMaterialObject.GetProp"/> method. The overall enthalpy of the stram is /// calculated using <see cref = "ICapeThermoMaterialObject.CalcProp"/> method. The unit then combines the flows, /// calculates the output stream enthalpy, and determines output pressure from the lower of /// the two streams' pressure and pressure drop parameter. Lastly, the results of the /// calculations are applied to the output material object using the <see cref = "ICapeThermoMaterialObject.SetProp"/> /// method. The last method of the calculate method is to call the material's /// <see cref = "ICapeThermoMaterialObject.CalcEquilibrium"/> method.</para> /// <para> /// In this case, the inlet materials need to be released. This is accomplished using the /// <see cref = "System.Runtime.InteropServices.Marshal.ReleaseComObject"/> method. /// Using this method to release the outlet material object would result in an null object reference error. /// </para> /// <para> /// The method also documents use of the <see cref = "CapeObjectBase.throwException"/> method to provide /// CAPE-OPEN compliant error handling. /// </para> /// <code> /// protected override void Calculate() /// { /// // Log a message using the simulation context (pop-up message commented out. /// if (this.SimulationContext != null) /// ((ICapeDiagnostic)this.SimulationContext).LogMessage("Starting Mixer Calculation"); /// //(CapeOpen.ICapeDiagnostic)(this.SimulationContext).PopUpMessage("Starting Mixer Calculation"); /// /// // Get the material Object from Port 0. /// ICapeThermoMaterialObject in1 = null; /// ICapeThermoMaterialObject tempMO = null; /// try /// { /// tempMO = (ICapeThermoMaterialObject)this.Ports[0].connectedObject; /// } /// catch (System.Exception p_Ex) /// { /// this.OnUnitOperationEndCalculation("Error - Material object does not support CAPE-OPEN Thermodynamics 1.0."); /// CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Material object does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); /// this.throwException(ex); /// } /// /// // Duplicate the port, its an input port, always use a duplicate. /// try /// { /// in1 = (ICapeThermoMaterialObject)tempMO.Duplicate(); /// } /// catch (System.Exception p_Ex) /// { /// this.OnUnitOperationEndCalculation("Error - Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0."); /// CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); /// this.throwException(ex); /// } /// // Arrays for the GetProps and SetProps call for enthaply. /// String[] phases = { "Overall" }; /// String[] props = { "enthalpy" }; /// /// // Declare variables for calculations. /// String[] in1Comps = null; /// double[] in1Flow = null; /// double[] in1Enthalpy = null; /// double[] pressure = null; /// double totalFlow1 = 0; /// /// // Exception catching code... /// try /// { /// // Get Strings, must cast to string array data type. /// in1Comps = (String[])in1.ComponentIds; /// /// // Get flow. Arguments are the property; phase, in this case, Overall; compound identifications /// // in this case, the null returns the property for all components; calculation type, in this case, /// // null, no calculation type; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. /// in1Flow = (double[])in1.GetProp("flow", "Overall", null, null, "mole"); /// /// // Get pressure. Arguments are the property; phase, in this case, Overall; compound identifications /// // in this case, the null returns the property for all components; calculation type, in this case, the /// // mixture; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. /// pressure = (double[])in1.GetProp("Pressure", "Overall", null, "Mixture", null); /// /// // The following code adds the individual flows to get the total flow for the stream. /// for (int i = 0; i < in1Flow.Length; i++) /// { /// totalFlow1 = totalFlow1 + in1Flow[i]; /// } /// /// // Calculates the mixture enthalpy of the stream. /// in1.CalcProp(props, phases, "Mixture"); /// /// // Get the enthalpy of the stream. Arguments are the property, enthalpy; the phase, overall; /// // a null pointer, required as the overall enthalpy is desired; the calculation type is /// // mixture; and the basis is moles. /// in1Enthalpy = (double[])in1.GetProp("enthalpy", "Overall", null, "Mixture", "mole"); /// } /// catch (System.Exception p_Ex) /// { /// // Exception handling, wraps a COM exception, shows the message, and re-throws the excecption. /// if (p_Ex is System.Runtime.InteropServices.COMException) /// { /// System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; /// p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(in1, p_Ex); /// } /// this.throwException(p_Ex); /// } /// IDisposable disp = in1 as IDisposable; /// if (disp != null) /// { /// disp.Dispose(); /// } /// /// /// // Get the material Object from Port 0. /// ICapeThermoMaterialObject in2 = null; /// tempMO = null; /// try /// { /// tempMO = (ICapeThermoMaterialObject)this.Ports[1].connectedObject; /// } /// catch (System.Exception p_Ex) /// { /// this.OnUnitOperationEndCalculation("Error - Material object does not support CAPE-OPEN Thermodynamics 1.0."); /// CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Material object does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); /// this.throwException(ex); /// } /// /// // Duplicate the port, its an input port, always use a duplicate. /// try /// { /// in2 = (ICapeThermoMaterialObject)tempMO.Duplicate(); /// } /// catch (System.Exception p_Ex) /// { /// this.OnUnitOperationEndCalculation("Error - Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0."); /// CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); /// this.throwException(ex); /// } /// /// // Declare variables. /// String[] in2Comps = null; /// double[] in2Flow = null; /// double[] in2Enthalpy = null; /// double totalFlow2 = 0; /// /// // Try block. /// try /// { /// // Get the component identifications. /// in2Comps = in2.ComponentIds; /// /// // Get flow. Arguments are the property; phase, in this case, Overall; compound identifications /// // in this case, the null returns the property for all components; calculation type, in this case, /// // null, no calculation type; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. /// in2Flow = in2.GetProp("flow", "Overall", null, null, "mole"); /// /// // Get pressure. Arguments are the property; phase, in this case, Overall; compound identifications /// // in this case, the null returns the property for all components; calculation type, in this case, the /// // mixture; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. /// double[] press = in2.GetProp("Pressure", "Overall", null, "Mixture", null); /// if (press[0] < pressure[0]) pressure[0] = press[0]; /// /// // The following code adds the individual flows to get the total flow for the stream. /// for (int i = 0; i < in2Flow.Length; i++) /// { /// totalFlow2 = totalFlow2 + in2Flow[i]; /// } /// /// // Calculates the mixture enthalpy of the stream. /// in2.CalcProp(props, phases, "Mixture"); /// /// // Get the enthalpy of the stream. Arguments are the property, enthalpy; the phase, overall; /// // a null pointer, required as the overall enthalpy is desired; the calculation type is /// // mixture; and the basis is moles. /// in2Enthalpy = in2.GetProp("enthalpy", "Overall", null, "Mixture", "mole"); /// } /// catch (System.Exception p_Ex) /// { /// System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; /// if (comException != null) /// { /// p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(in2, p_Ex); /// } /// this.throwException(p_Ex); /// } /// // Release the material object if it is a COM object. /// disp = in2 as IDisposable; /// if (disp != null) /// { /// disp.Dispose(); /// } /// /// /// // Get the outlet material object. /// ICapeThermoMaterialObject outPort = (ICapeThermoMaterialObject)this.Ports[2].connectedObject; /// /// // An empty, one-member array to set values in the outlet material stream. /// double[] values = new double[1]; /// /// // Use energy balanace to calculate the outlet enthalpy. /// values[0] = (in1Enthalpy[0] * totalFlow1 + in2Enthalpy[0] * totalFlow2) / (totalFlow1 + totalFlow2); /// try /// { /// // Set the outlet enthalpy, for the overall phase, with a mixture calculation type /// // to the value calculated above. /// outPort.SetProp("enthalpy", "Overall", null, "Mixture", "mole", values); /// /// // Set the outlet pressure to the lower of the to inlet pressures less the value of the /// // pressure drop parameter. /// pressure[0] = pressure[0] - (((RealParameter)(this.Parameters[0])).SIValue); /// /// // Set the outlet pressure. /// outPort.SetProp("Pressure", "Overall", null, null, null, pressure); /// /// // Resize the value array for the number of components. /// values = new double[in1Comps.Length]; /// /// // Calculate the individual flow for each component. /// for (int i = 0; i < in1Comps.Length; i++) /// { /// values[i] = in1Flow[i] + in2Flow[i]; /// } /// // Set the outlet flow by component. Note, this is for overall phase and mole flows. /// // The component Identifications are used as a check. /// outPort.SetProp("flow", "Overall", in1Comps, null, "mole", values); /// /// // Calculate equilibrium using a "pressure-enthalpy" flash type. /// outPort.CalcEquilibrium("PH", null); /// /// // Release the material object if it is a COM object. /// if (outPort.GetType().IsCOMObject) /// { /// System.Runtime.InteropServices.Marshal.FinalReleaseComObject(outPort); /// } /// } /// catch (System.Exception p_Ex) /// { /// System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; /// if (comException != null) /// { /// p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(outPort, p_Ex); /// } /// this.throwException(p_Ex); /// } /// /// // Log the end of the calculation. /// if (this.SimulationContext != null) /// ((CapeOpen.ICapeDiagnostic)this.SimulationContext).LogMessage("Ending Mixer Calculation"); /// //(CapeOpen.ICapeDiagnostic)(this.SimulationContext).PopUpMessage("Ending Mixer Calculation"); /// } /// /// </code> /// </example> /// <see cref="ICapeThermoMaterialObject"/> /// <see cref="COMExceptionHandler"/> protected override void Calculate() { // Log a message using the simulation context (pop-up message commented out. if (this.SimulationContext != null) { ((ICapeDiagnostic)this.SimulationContext).LogMessage("Starting Mixer Calculation"); } //(CapeOpen.ICapeDiagnostic>(this.SimulationContext).PopUpMessage("Starting Mixer Calculation"); // Get the material Object from Port 0. ICapeThermoMaterialObject in1 = null; ICapeThermoMaterialObject tempMO = null; try { tempMO = (ICapeThermoMaterialObject)this.Ports[0].connectedObject; } catch (System.Exception p_Ex) { this.OnUnitOperationEndCalculation("Error - Material object does not support CAPE-OPEN Thermodynamics 1.0."); CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Material object does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); this.throwException(ex); } // Duplicate the port, its an input port, always use a duplicate. try { in1 = (ICapeThermoMaterialObject)tempMO.Duplicate(); } catch (System.Exception p_Ex) { this.OnUnitOperationEndCalculation("Error - Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0."); CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); this.throwException(ex); } // Arrays for the GetProps and SetProps call for enthaply. String[] phases = { "Overall" }; String[] props = { "enthalpy" }; // Declare variables for calculations. String[] in1Comps = null; double[] in1Flow = null; double[] in1Enthalpy = null; double[] pressure = null; double totalFlow1 = 0; // Exception catching code... try { // Get Strings, must cast to string array data type. in1Comps = (String[])in1.ComponentIds; // Get flow. Arguments are the property; phase, in this case, Overall; compound identifications // in this case, the null returns the property for all components; calculation type, in this case, // null, no calculation type; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. in1Flow = (double[])in1.GetProp("flow", "Overall", null, null, "mole"); // Get pressure. Arguments are the property; phase, in this case, Overall; compound identifications // in this case, the null returns the property for all components; calculation type, in this case, the // mixture; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. pressure = (double[])in1.GetProp("Pressure", "Overall", null, "Mixture", null); // The following code adds the individual flows to get the total flow for the stream. for (int i = 0; i < in1Flow.Length; i++) { totalFlow1 = totalFlow1 + in1Flow[i]; } // Calculates the mixture enthalpy of the stream. in1.CalcProp(props, phases, "Mixture"); // Get the enthalpy of the stream. Arguments are the property, enthalpy; the phase, overall; // a null pointer, required as the overall enthalpy is desired; the calculation type is // mixture; and the basis is moles. in1Enthalpy = (double[])in1.GetProp("enthalpy", "Overall", null, "Mixture", "mole"); } catch (System.Exception p_Ex) { // Exception handling, wraps a COM exception, shows the message, and re-throws the excecption. if (p_Ex is System.Runtime.InteropServices.COMException) { System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(in1, p_Ex); } this.throwException(p_Ex); } IDisposable disp = in1 as IDisposable; if (disp != null) { disp.Dispose(); } // Get the material Object from Port 0. ICapeThermoMaterialObject in2 = null; tempMO = null; try { tempMO = (ICapeThermoMaterialObject)this.Ports[1].connectedObject; } catch (System.Exception p_Ex) { this.OnUnitOperationEndCalculation("Error - Material object does not support CAPE-OPEN Thermodynamics 1.0."); CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Material object does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); this.throwException(ex); } // Duplicate the port, its an input port, always use a duplicate. try { in2 = (ICapeThermoMaterialObject)tempMO.Duplicate(); } catch (System.Exception p_Ex) { this.OnUnitOperationEndCalculation("Error - Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0."); CapeOpen.CapeInvalidOperationException ex = new CapeOpen.CapeInvalidOperationException("Object connected to Inlet Port 1 does not support CAPE-OPEN Thermodynamics 1.0.", p_Ex); this.throwException(ex); } // Declare variables. String[] in2Comps = null; double[] in2Flow = null; double[] in2Enthalpy = null; double totalFlow2 = 0; // Try block. try { // Get the component identifications. in2Comps = in2.ComponentIds; // Get flow. Arguments are the property; phase, in this case, Overall; compound identifications // in this case, the null returns the property for all components; calculation type, in this case, // null, no calculation type; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. in2Flow = in2.GetProp("flow", "Overall", null, null, "mole"); // Get pressure. Arguments are the property; phase, in this case, Overall; compound identifications // in this case, the null returns the property for all components; calculation type, in this case, the // mixture; and lastly, the basis, moles. Result is cast to a double array, and will contain one value. double[] press = in2.GetProp("Pressure", "Overall", null, "Mixture", null); if (press[0] < pressure[0]) { pressure[0] = press[0]; } // The following code adds the individual flows to get the total flow for the stream. for (int i = 0; i < in2Flow.Length; i++) { totalFlow2 = totalFlow2 + in2Flow[i]; } // Calculates the mixture enthalpy of the stream. in2.CalcProp(props, phases, "Mixture"); // Get the enthalpy of the stream. Arguments are the property, enthalpy; the phase, overall; // a null pointer, required as the overall enthalpy is desired; the calculation type is // mixture; and the basis is moles. in2Enthalpy = in2.GetProp("enthalpy", "Overall", null, "Mixture", "mole"); } catch (System.Exception p_Ex) { System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; if (comException != null) { p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(in2, p_Ex); } this.throwException(p_Ex); } // Release the material object if it is a COM object. disp = in2 as IDisposable; if (disp != null) { disp.Dispose(); } // Get the outlet material object. ICapeThermoMaterialObject outPort = (ICapeThermoMaterialObject)this.Ports[2].connectedObject; // An empty, one-member array to set values in the outlet material stream. double[] values = new double[1]; // Use energy balanace to calculate the outlet enthalpy. values[0] = (in1Enthalpy[0] * totalFlow1 + in2Enthalpy[0] * totalFlow2) / (totalFlow1 + totalFlow2); try { // Set the outlet enthalpy, for the overall phase, with a mixture calculation type // to the value calculated above. outPort.SetProp("enthalpy", "Overall", null, "Mixture", "mole", values); // Set the outlet pressure to the lower of the to inlet pressures less the value of the // pressure drop parameter. pressure[0] = pressure[0] - (((RealParameter)(this.Parameters[0])).SIValue); // Set the outlet pressure. outPort.SetProp("Pressure", "Overall", null, null, null, pressure); // Resize the value array for the number of components. values = new double[in1Comps.Length]; // Calculate the individual flow for each component. for (int i = 0; i < in1Comps.Length; i++) { values[i] = in1Flow[i] + in2Flow[i]; } // Set the outlet flow by component. Note, this is for overall phase and mole flows. // The component Identifications are used as a check. outPort.SetProp("flow", "Overall", in1Comps, null, "mole", values); // Calculate equilibrium using a "pressure-enthalpy" flash type. outPort.CalcEquilibrium("PH", null); // Release the material object if it is a COM object. if (outPort.GetType().IsCOMObject) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(outPort); } } catch (System.Exception p_Ex) { System.Runtime.InteropServices.COMException comException = (System.Runtime.InteropServices.COMException)p_Ex; if (comException != null) { p_Ex = CapeOpen.COMExceptionHandler.ExceptionForHRESULT(outPort, p_Ex); } this.throwException(p_Ex); } // Log the end of the calculation. if (this.SimulationContext != null) { ((CapeOpen.ICapeDiagnostic) this.SimulationContext).LogMessage("Ending Mixer Calculation"); } //(CapeOpen.ICapeDiagnostic>(this.SimulationContext).PopUpMessage("Ending Mixer Calculation"); }