Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specifing fluid with custom engine and non-standard fluid specification #597

Open
LoGaL93 opened this issue Jan 16, 2025 · 6 comments
Open

Comments

@LoGaL93
Copy link

LoGaL93 commented Jan 16, 2025

Hello,

I would like to integrate our fluid properties libraries with Tespy. I understand it is necessary to create a custom wrapper, with all the necessary functions to calculate Temperature ecc. This is fine.

I am having issues with the specification of the fluid itself. As far as I understand by digging around, it is somehow hard coded that the input format must be fluid = {'Component1' : Conc1, 'Component2': Conc2} and fluid_engine = customengine.

Some of our libraries specify the fluid as a set of molecular groups instead, for instance n-Butane would be defined as 2CH3 + 2CH2, or as 2 arrays: V1 = [CH2, CH3], V2 = [2,2]. A mixture of n-components would instead require 2 2D arrays + an additional 1D array for the concentrations. Speaking of mixtures, I don't want to use mixing rules, as our models directly compute mixture properties.

Isn't it possible to somehow pass an instance of the wrapper? As an example:

import FluidPropWrapper

WrapperInstance = FluidPropWrapper('Lib' = 'HOGC-PCP-SAFT', 'Groups' = [CH3, CH2], N = [2,2]) 

c1.set_attr(
    m=1, p=10, T=600,
    wrapper_instance = WrapperInstance
)

Any other solution (apart from coming up with a very long fluid name string containing all the info :D) would be also nice :)

Thanks,

Lorenzo

@fwitte
Copy link
Member

fwitte commented Jan 17, 2025

Hi @LoGaL93,

in principle you should be able to just handle that mixture thing inside your custom FluidWrapper class. You could do something like this following https://tespy.readthedocs.io/en/main/modules/fluid_properties.html#implementing-a-custom-engine:

wrapper = FluidPropWrapper(Lib='HOGC-PCP-SAFT', Groups=[CH3, CH2], N=[2,2])

c1.set_attr(
    m=1, p=10, T=600,
    fluid={"NameOfFluid": 1}, fluid_engines={"NameOfFluid": wrapper}

)

From the tespy point of view your fluid will be a pure fluid, which is fine if

  • you do all the mixture properties on the Wrapper side
  • you do not need to have variable mass/mole fractions of the different components of the fluid in parts of your network (e.g. like you would have with a combustion engine)

Does that work for you?

Best

Francesco

@LoGaL93
Copy link
Author

LoGaL93 commented Jan 17, 2025

Ok I see, so it is possible to provide as input an instance of the custom wrapper class, rather than the class itself. From the custom engine tutorial, it seemed you imported the class and gave that as input without creating a new object.

Well, for the time being it is fine.However, is there a way to give custom mixing functions? (where one would create a mixture object and the extract the properties). I guess that could be useful also with refprop or coolprop

@LoGaL93
Copy link
Author

LoGaL93 commented Jan 17, 2025

ok, seems it is possible? In principle I would just need to add the custom "fake" mixing rule functions to those dictionaries at the end of fluid_properties/mixtures.py?

@fwitte
Copy link
Member

fwitte commented Jan 18, 2025

Not sure if I got this: if you do all the mixture property handling inside your own class, and make the fluid "pure" for tespy with the mixture being the only component, then you do not need to add anything to the mixing rules of tespy. Those rules are only applied to fluids which are not "pure" from tespy's perspective (i.e. more than one component in the fluid vector specified to a connection).

@fwitte
Copy link
Member

fwitte commented Jan 18, 2025

Ok I see, so it is possible to provide as input an instance of the custom wrapper class, rather than the class itself. From the custom engine tutorial, it seemed you imported the class and gave that as input without creating a new object.

Ah I see, you are likely correct here, I did not think of that. A quite hacky way to inject your mixture parameters would be to "hide" them inside the string fluid name and then split the string inside the __init__ method of your Wrapper class. I think there might be a need for some reworking of the Wrapper signature to be able to pass more arguments to the Wrapper class from inside tespy.

Would it be possible for you to share a minimal example here?

@LoGaL93
Copy link
Author

LoGaL93 commented Jan 18, 2025

Yes, I eventually came up with quite a hacky fluid name that hides all the relevant information. It is not elegant but it works (at least I could validate with the basic heat pump model using butane).

fluid_input_dict = {'nCmp':1,
              'cnc': [1],
              'Groups': [['CH2', 'CH3']],
              'Groups Occ': [[2,2]]}

fp_fluid_network= Network()

---- setting my basic heat pump network, skipping the code part ---
 fluid_name = 'pseudofluid##HOGC-PCP-SAFT##' + str(fluid_input_dict)
c2.set_attr(T=20, x=1, fluid={fluid_name: 1}, fluid_engines ={fluid_name: FPTespyWrapper})

Then eventually in the wrapper I split everything again:

from ast import literal_eval

class FPTespyWrapper(FluidPropertyWrapper):

    def __init__(self, fluid, back_end = None) -> None:
        self.EoS = None
        data = fluid.split('##')

        if len(data) == 2: # normal fluid or mixture with names and concentrations
            --- skipping not relevant code --
        elif len(data) == 3: # fluid where other type of parameters are specified, such as homosegmented PCP-SAFT
            if data[0] == 'pseudofluid':
                library = data[1]
                fluid_name = 'pseudofluid'
                inputs = literal_eval(data[2])
                if library == 'HOGC-PCP-SAFT':
                    n_cmp = inputs['nCmp']
                    cnc = inputs['cnc']
                    groups =  inputs['Groups']
                    groups_occurrences = inputs['Groups Occ']
           --- doing rest of my stuff ---
                   

I believe it could also be possible to implement fake mixing rules, whereby instead of evaluating the property of each mixture component and then applying a mixing rule, a pseudo-pure mixture is created starting from some data stored in the wrapper of each component. This is then directly used to calculate the requested property. In this way there's no need of treating the mixture as a pure fluid with concentration 1.
This same approach could be used with Refprop mixtures as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants