Has anyone had experience scripting the creation of a MaterialStream with a composition that was generated using the Petroleum Characterization utility? If so, I’d be grateful to see an example.
I’m trying to create a custom tool that accepts, for example, a given bulk MW and SG, generates an oil composition (pseudocomponents), creates a MaterialStream with a given flow rate, and flashes that stream to a given T and P to arrive at liquid and vapor phase flow rates. Ultimately, I will need to mix in the C1 – C6 light ends prior to the flash as well.
Thanks in advance for your help.
Tom
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
While I was working with the new cross-platform user interface, I've created a helper function which takes these bulk parameters and returns a list of compounds:
Thanks, Daniel. Has this new helper function made it into DTL_v5.2.6836? I tried reproducing the oil characterization screenshot example from the DWSIM User Guide, and I receive a "No method matches given arguments" error when calling the helper function. Snippets of my code and the error are below.
import clr
clr.AddReference("DWSIM.Thermodynamics.StandaloneLibrary")
from DWSIM.Thermodynamics.Utilities.PetroleumCharacterization import GenerateCompounds
Yes, I can cast those values to Nullable[Double], but I can't set them to None (i.e. I can't nullify them), because then their type is converted to NoneType and the call to GenerateCompounds fails. The following snippet works, but values of 0.0 are not the same as None (a.k.a. Nothing in VB), so is that safe behavior? I wouldn't think so.
Anyway, for this specific function there is no problem on having a value as long as it is zero (https://github.com/DanWBR/dwsim5/blob/windows/DWSIM.Thermodynamics/PetroleumCharacterization/GenerateCompounds.vb#L43)
Last edit: Daniel Medeiros 2018-10-25
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Still working on this a little bit at a time. May I offer a couple of observations?
First, it appears that the no arg MaterialStream () constructor does not initialize the Phases attribute; one must call MaterialStream (String, String) instead. That fact might be good to note in the API documentation. If the instantiation of mixture (see below) were made using the no arg MaterialStream () constructor, then no pseudocomponents would be added to mixture, because material_stream.Phases.get_Count() would equal 0, and any subsequent flash (not shown here) would fail. The following snippets do appear to work correctly.
if overall_composition:
compound_names = [components[0] for components in overall_composition]
mole_fractions = [components[1] for components in overall_composition]
mixture = flash_calculator.CreateMaterialStream(compound_names, mole_fractions)
elif oil_characterization:
compounds, assay = oil_characterization
compound_names = [compound.Name for compound in compounds]
mole_fractions = [compound.MoleFraction for compound in compounds]
mixture = MaterialStream("", "")
DWSIMWrapper._add_pseudocomponents_to_material_stream(compounds, assay, mixture)
mixture.SetOverallComposition(Array[Double](mole_fractions))
@classmethod
def _add_pseudocomponents_to_material_stream(cls, pseudocomponents, assay, material_stream):
try:
# add pseudocomponents as new Compound objects to each phase
for component in pseudocomponents:
for phase_index in range(0, material_stream.Phases.get_Count()):
compound = Compound(component.Name, "")
compound.ConstantProperties = component.ConstantProperties
material_stream.Phases[phase_index].Compounds.Add(compound.Name, compound)
# Quality check requires flowsheet object to exist
# qc = QualityCheck(assay, material_stream)
# qc.DoQualityCheck()
except Exception as e:
raise e
This leads me to my second observation: It appears that DoQualityCheck () was designed specifically to be used within the GUI. It might be useful to make the raw error calculations available as a standalone method, and then have something like ReportQualityCheck () write the comparison to a string/report/screen.
Last edit: Tom 2018-10-29
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Yes, there is no empty MS constructor as DWSIM thermo wasn't originally designed to be used from outside the GUI. That is easily fixed, as you may know.
Regarding the DoQualityCheck() issue, the answer is yes again for the same reasons. Also easily fixed.
I'll reply to this post as soon as I have updated the code with your suggestions.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I'm now working on mixing the light ends into the oil characterization. I see from Mixer.vb that the StreamMixer unit operation is also fairly tightly coupled with UI code. I was, therefore, planning to just create my own Python method to mix the streams. However, if you have an example of how this should be done most efficiently in Python, that would be very helpful.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I believe that the current method on Mixer.vb is the most efficient one. Create a new stream, set its composition and mass flow through a mass balance. Calculate and set enthalpy through an energy balance and calculate the temperature using a PH flash, with the pressure being an assumed value. Maybe in your case you don't even have to calculate temperature, just set it to a default value...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello
Has anyone had experience scripting the creation of a MaterialStream with a composition that was generated using the Petroleum Characterization utility? If so, I’d be grateful to see an example.
I’m trying to create a custom tool that accepts, for example, a given bulk MW and SG, generates an oil composition (pseudocomponents), creates a MaterialStream with a given flow rate, and flashes that stream to a given T and P to arrive at liquid and vapor phase flow rates. Ultimately, I will need to mix in the C1 – C6 light ends prior to the flash as well.
Thanks in advance for your help.
Tom
Hi Tom,
While I was working with the new cross-platform user interface, I've created a helper function which takes these bulk parameters and returns a list of compounds:
https://github.com/DanWBR/dwsim5/blob/windows/DWSIM.UI.Desktop.Editors/Compounds/BulkC7.cs#L173
The remaining work depends if you're going to start with a predefined simulation or from scratch.
Daniel
Last edit: Daniel Medeiros 2018-10-25
Thanks, Daniel. Has this new helper function made it into DTL_v5.2.6836? I tried reproducing the oil characterization screenshot example from the DWSIM User Guide, and I receive a "No method matches given arguments" error when calling the helper function. Snippets of my code and the error are below.
import clr
clr.AddReference("DWSIM.Thermodynamics.StandaloneLibrary")
from DWSIM.Thermodynamics.Utilities.PetroleumCharacterization import GenerateCompounds
class DWSIMWrapper():
@classmethod
def flash_t_p(cls, flash_to_temperature, flash_to_pressure, overall_molar_flowrate, overall_composition):
assay_name = "Heavy Ends"
ncomps = 10
t1 = None
t2 = None
v1 = None
v2 = None
mw = None
sg = 0.87
nbp = None
mw0 = 80.0
sg0 = 0.70
nbp0 = 333.0
Tccorr = "Riazi-Daubert (1985)"
Pccorr = "Riazi-Daubert (1985)"
AFcorr = "Lee-Kesler (1976)"
MWcorr = "Winn (1956)"
adjustAf = True
adjustZR = True
comps = GenerateCompounds()
comp_results = comps.GenerateCompounds(assay_name, ncomps, Tccorr, Pccorr, AFcorr, MWcorr, adjustAf, adjustZR, mw, sg, nbp, v1, v2, t1, t2, mw0, sg0, nbp0)
running thermo tests E............ ====================================================================== ERROR: test_flash_t_p (tests.TestDWSIMWrapper) ---------------------------------------------------------------------- Traceback (most recent call last): File "T:\PETMIN\TOrtiz\projects\audit-excel-spreadsheets\thermo\tests.py", line 54, in test_flash_t_p DWSIMWrapper.flash_t_p(self.temperature, self.pressure, self.molar_flowrate, self.composition) File "T:\PETMIN\TOrtiz\projects\audit-excel-spreadsheets\thermo\dwsim_wrapper.py", line 45, in flash_t_p comp_results = comps.GenerateCompounds(assay_name, ncomps, Tccorr, Pccorr, AFcorr, MWcorr, adjustAf, adjustZR, mw, sg, nbp, v1, v2, t1, t2, mw0, sg0, nbp0) TypeError: No method matches given arguments
Are you running this from inside DWSIM itself? It is on DTL v5.2 anyway...
Check which variables are of the Nullable(Of Double) type and adjust them on your call accordingly: https://github.com/DanWBR/dwsim5/blob/windows/DWSIM.Thermodynamics/PetroleumCharacterization/GenerateCompounds.vb#L30
No, I'm running this from a standalone Python script using pythonnet.
Yes, I can cast those values to Nullable[Double], but I can't set them to None (i.e. I can't nullify them), because then their type is converted to NoneType and the call to GenerateCompounds fails. The following snippet works, but values of 0.0 are not the same as None (a.k.a. Nothing in VB), so is that safe behavior? I wouldn't think so.
from System import Double, Array, Nullable
(Pdb) pp(comp_values)
[<dwsim.thermodynamics.baseclasses.compound object="" at="" 0x000001d6581e17b8="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e17f0="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound object="" at="" 0x000001d6581e1828="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e1860="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e1898="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e18d0="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound object="" at="" 0x000001d6581e1908="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e1940="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e1978="" object="" at="">,
<dwsim.thermodynamics.baseclasses.compound 0x000001d6581e19b0="" object="" at="">]</dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound></dwsim.thermodynamics.baseclasses.compound>
(Pdb) pp(comp_values[0])
<dwsim.thermodynamics.baseclasses.compound object="" at="" 0x000001d6581e17b8="">
(Pdb) pp(comp_values[0].Name)
'Heavy Ends_NBP431'
(Pdb) pp(comp_values[0].MoleFraction)
0.10004146986435093
(Pdb)</dwsim.thermodynamics.baseclasses.compound>
Does it work if you cast to
Nullable[Double]()
?Anyway, for this specific function there is no problem on having a value as long as it is zero (https://github.com/DanWBR/dwsim5/blob/windows/DWSIM.Thermodynamics/PetroleumCharacterization/GenerateCompounds.vb#L43)
Last edit: Daniel Medeiros 2018-10-25
I can't assign to Nullable[Double] (), because Nullable< T > doesn't have a default, no-arg constructor: https://docs.microsoft.com/en-us/dotnet/api/system.nullable-1?view=netframework-4.7.2 . The only available constructor takes a single, value type argument. See also error below.
However, if 0.0 is a safe default value for the nullable arguments of GenerateCompounds, then that will work for me. Thanks for your help.
Traceback (most recent call last):
File "T:\PETMIN\TOrtiz\projects\audit-excel-spreadsheets\thermo\tests.py", line 54, in test_flash_t_p
DWSIMWrapper.flash_t_p(self.temperature, self.pressure, self.molar_flowrate, self.composition)
File "T:\PETMIN\TOrtiz\projects\audit-excel-spreadsheets\thermo\dwsim_wrapper.py", line 23, in flash_t_p
t1 = Nullable[Double] ()
SystemError: <class culture="neutral," 'system.0,="" publickeytoken="b77a5c561934e089]]'"> returned NULL without setting an error</class>
Last edit: Tom 2018-10-26
Still working on this a little bit at a time. May I offer a couple of observations?
First, it appears that the no arg MaterialStream () constructor does not initialize the Phases attribute; one must call MaterialStream (String, String) instead. That fact might be good to note in the API documentation. If the instantiation of mixture (see below) were made using the no arg MaterialStream () constructor, then no pseudocomponents would be added to mixture, because material_stream.Phases.get_Count() would equal 0, and any subsequent flash (not shown here) would fail. The following snippets do appear to work correctly.
This leads me to my second observation: It appears that DoQualityCheck () was designed specifically to be used within the GUI. It might be useful to make the raw error calculations available as a standalone method, and then have something like ReportQualityCheck () write the comparison to a string/report/screen.
Last edit: Tom 2018-10-29
Hi Tom,
Yes, there is no empty MS constructor as DWSIM thermo wasn't originally designed to be used from outside the GUI. That is easily fixed, as you may know.
Regarding the DoQualityCheck() issue, the answer is yes again for the same reasons. Also easily fixed.
I'll reply to this post as soon as I have updated the code with your suggestions.
Here's an updated build: https://gum.co/VHBNe
Thanks, Daniel.
I'm now working on mixing the light ends into the oil characterization. I see from Mixer.vb that the StreamMixer unit operation is also fairly tightly coupled with UI code. I was, therefore, planning to just create my own Python method to mix the streams. However, if you have an example of how this should be done most efficiently in Python, that would be very helpful.
I believe that the current method on Mixer.vb is the most efficient one. Create a new stream, set its composition and mass flow through a mass balance. Calculate and set enthalpy through an energy balance and calculate the temperature using a PH flash, with the pressure being an assumed value. Maybe in your case you don't even have to calculate temperature, just set it to a default value...
OK. Is the Mixer class exposed in DTL_v5.4.6878.2 ? I was having trouble importing it from DWSIM.UnitOperations.UnitOperations .
Nops. it is on DWSIM.UnitOperations.dll. You may try importing it from a DWSIM installation, but it will probably ask for some additional DLLs.
Would that alter the licensing terms for any code I write if I were to link to any DWSIM libraries other than the Standalone Thermodynamics Library?
Last edit: Tom 2018-11-05
In this case, yes. You'll have to write your own code. What's your difficulty on writing the mixer code? Something specific?
No, no problem. I was just trying to avoid reinventing the wheel. The following appears to be working for me:
I see. I've added two new functions to help setting the stream molar composition: https://github.com/DanWBR/dwsim5/blob/windows/DWSIM.Thermodynamics/Material%20Stream/MaterialStream.vb#L934