How to calculate the mass of gas in a tank?

  • #71
I tried something new and corrected the measured pressure of the CO##_2## tank with the atmospheric pressure. I expected to see a saw like pattern and hopefully small peaks dissapear, but got the plots attached here. The x axis is the Unix epoch time and the y axis is the difference between tank pressure and atmospheric pressure. Unfortunately the atmospheric pressure sensor is some 300 meters apart from the tanks.
I am rather discouraged, because we see a rising pressure over a few days and over three months the pressure difference never gets to 0, where I expect a change of tanks to have happened.

Maybe the whole experimental setup is faulty and needs revisiting?
 

Attachments

  • PressureDifference.png
    PressureDifference.png
    23.5 KB · Views: 14
  • PressureDifferenceZoom.png
    PressureDifferenceZoom.png
    11.8 KB · Views: 15
Science news on Phys.org
  • #73
Leopold89 said:
##T=297.138375\mathrm{K}, p=5438467.5\mathrm{Pa}, m_0= 4.84398743373488\mathrm{kg}##
Leopold89 said:
Yes. After your explanation (from ChesterMiller) I checked my data over the last three months and found that for example the CO##_2## tank has a maximum pressure of 6.1MPa.


From this data, we have established that the mass is initially all in the gaseous state according to this table of saturated ##\mathrm{CO_2}## because at 24ºC (297 K) you're below the saturation pressure.
1724691853391.png


I'm not yet ready to study the evolution of the system as you discharge the content of the tank but I think I can calculate the initial mass from the experimental data you have.
Leopold89 said:
I'm using a different approach to the one you used there and got a different result too.

Using the ideal gas law being so close to the saturation bell doesn't give accurate results but there is an adapted formula for that.

1724692153792.png


1724692163335.png

1724692170159.png


The critical-point of ##\mathrm{CO_2}## is ##P_{cr}=7.39 \ \mathrm{MPa}## and ##T_{cr}=304.2 \ \mathrm{K}##.
1724692790448.png


Therefore, from your data, the reduced pressure and reduced temperature are:
$$T_R = \frac{T}{T_{cr}}=\frac{297.1}{304.2}=0.98$$
$$P_R = \frac{P}{P_{cr}}=\frac{5.43}{7.39}=0.75$$
So your compressibility factor ##Z## should be around ##Z=0.64##.
1724693504711.png


From the definition of ##Z##, it is possible to obtain the specific volume of the gas.
(Using SI units whenever units don't cancel)
$$Z = \frac{Pv}{RT} \rightarrow v = \frac{ZRT}{P}=\frac{0.64*188.9*297.1}{5.43*10^6}=0.006615 \ \mathrm{m^3/kg}$$
Lastly, knowing the tank's volume is ##V = 0.05 \ \mathrm{m^3}## we can find the actual mass.
$$v=V/m \rightarrow m=V/v=\frac{0.05}{0.006615}=7.56 \ \mathrm{kg}$$
I'm not sure if it's my first or second time in my life doing these calculations but I followed the book "Thermodynamics An Engineering Approach" by the letter so I believe it'd be OK. Feel free to point out any errors or comments.
 
Last edited:
  • #74
Juanda said:
I'm not yet ready to study the evolution of the system as you discharge the content of the tank but I think I can calculate the initial mass from the experimental data you have.
As long as the content inside the tank is entirely gas, the approach from #73 should be valid—assuming what I wrote there is correct. Since you have pressure and temperature data from your sensors, you have all the information needed to determine the mass, which I believe is your primary concern. If you can incorporate the compressibility factor into your Python code, you'll be able to calculate the mass at each time step.
It would be helpful to add checks in your code to ensure that, as it runs, it verifies whether you're inside or outside the saturation bell. This way, you'll know if you're using the correct set of equations. Since the starting point doesn't have enough pressure to condense the gas, and you're releasing pressure over time, I believe you'll be able to use these equations most of the time because, once thermal equilibrium is reached, the content will likely be entirely gaseous.

I believe those green curves defining ##Z## correspond to a 3D surface ##Z(P, T)##. If you can find this surface, it should be fairly straightforward to add it to the code. If not, you could try approximating it with the level of detail necessary for your application.
 

Attachments

  • 1724696109964.png
    1724696109964.png
    4.9 KB · Views: 10
  • 1724696089229.png
    1724696089229.png
    4.2 KB · Views: 8
  • #75
Juanda said:
From this data, we have established that the mass is initially all in the gaseous state according to this table of saturated ##\mathrm{CO_2}## because at 24ºC (297 K) you're below the saturation pressure.
View attachment 350427

I'm not yet ready to study the evolution of the system as you discharge the content of the tank but I think I can calculate the initial mass from the experimental data you have.

I'm using a different approach to the one you used there and got a different result too.

Using the ideal gas law being so close to the saturation bell doesn't give accurate results but there is an adapted formula for that.

View attachment 350428

View attachment 350429
View attachment 350430

The critical-point of ##\mathrm{CO_2}## is ##P_{cr}=7.39 \ \mathrm{MPa}## and ##T_{cr}=304.2 \ \mathrm{K}##.
View attachment 350431

Therefore, from your data, the reduced pressure and reduced temperature are:
$$T_R = \frac{T}{T_cr}=\frac{297.1}{304.2}=0.98$$
$$P_R = \frac{P}{P_cr}=\frac{5.43}{7.39}=0.75$$
So your compressibility factor ##Z## should be around ##Z=0.64##.
View attachment 350432

From the definition of ##Z##, it is possible to obtain the specific volume of the gas.
(Using SI units whenever units don't cancel)
$$Z = \frac{Pv}{RT} \rightarrow v = \frac{ZRT}{P}=\frac{0.64*188.9*297.1}{5.43*10^6}=0.006615 \ \mathrm{m^3/kg}$$
Lastly, knowing the tank's volume is ##V = 0.05 \ \mathrm{m^3}## we can find the actual mass.
$$v=V/m \rightarrow m=V/v=\frac{0.05}{0.006615}=7.56 \ \mathrm{kg}$$
I'm not sure if it's my first or second time in my life doing these calculations but I followed the book "Thermodynamics An Engineering Approach" by the letter so I believe it'd be OK. Feel free to point out any errors or comments.
This is much better. Another approach would be to use the conditions in the table that are close to the initial temperature and pressure to estimate z: T=297 K, P=6.29 MPa, v=0.004327 m^3/kg=0.00019039 m^3/mole
$$z=\frac{(6290000)(0.00019039)}{(8.314)(297)}=0.49$$
 
  • #76
Like I said earlier, the vapor for the initial condition of CO2 is superheated. Therefore, as the tank begins to expel gas, the gas remaining in the tank will experience an isentropic expansion until the 2 phase envelope is readhed. Moran et al, Fundamentals of Engineering Thermodynamics, presents a pressure-enthalpy diagram for CO2 that can be used to follow the isentropic line down to the 2 phase envelope.
 
  • Informative
Likes Juanda
  • #77
Chestermiller said:
Like I said earlier, the vapor for the initial condition of CO2 is superheated. Therefore, as the tank begins to expel gas, the gas remaining in the tank will experience an isentropic expansion until the 2 phase envelope is readhed. Moran et al, Fundamentals of Engineering Thermodynamics, presents a pressure-enthalpy diagram for CO2 that can be used to follow the isentropic line down to the 2 phase envelope.

Is this the diagram you're referring to? It says R744 but that should be the same as CO2.
1724710325317.png


I'm still unsure of how to solve it but, from what you said, I can find the starting point from the known pressure and temperature.
Once it starts discharging, follow the isentropic line reducing pressure up to the new point read by the sensor.
From that, it'd be possible to know the vapor quality right after the release so we can find the portion of liquid and volume inside the tank. The densities of both are known since it's saturated and pressure and temperature are known so it's possible to calculate the mass.
During that process, I think I'd ignore the temperature sensor. We're already assuming it's got the same entropy so the temperature data is not necessary. My reason for ignoring it is its readings are indirect. I'd trust it when it has enough time to reach thermal equilibrium but not in cases like this.

1724711055788.png


We assumed an isentropic expansion which I believe also means it's adiabatic. It can be assumed that the heat input is not too significant during the discharging process. However, once the release is done and time passes, the tank will keep absorbing heat from the room because it's significantly cooler. As it absorbs heat, all the content will be transformed into gas now that the pressure is lower so it'd be possible to calculate the mass again but using the compressibility factor as mentioned in the previous post.

All this process (this and the procedure from post #73) completely bypasses the need for differential equations. However, to calculate everything in your Python code from your sensor data you'd need to add the functions from the diagrams we used. That is:
  • ##Z(P, T)##
  • All the properties that define the p-h diagram with the isolines.




PS.
In a closed system with a known mixture of saturated liquid and vapor at a known fixed volume, if I add a known amount of heat, what process does the mixture undergo? It can't be isochoric because, for that to happen, the system would need to be 100% vapor from the start. This detail is irrelevant for this exercise because sensors are available to obtain the necessary data (P and T). However, could I determine the path the mixture follows within the bell curve while heat is added at a fixed volume?
Perhaps I should start a new discussion to explore this point in more detail, rather than complicating this one further.
 
  • #78
Juanda said:
Before diving into thermodynamic properties and equations, would it be possible just to weigh the loaded and unloaded tank?

Leopold89 said:
But I get full tanks from the manufacturer
Leopold89 said:
In theory at some point in the future one could measure these masses, but not in the short term.
The way this thread is going, it looks like that 'future' is rapidly approaching!

Since the tanks are from an outside source, try getting a scale for one of the tanks and have a tank delivered to it (or moved to it). Maybe you will have to make room somewhere, or put it in an inconvenient spot. Try it out to see how well it works.

You may even be able to rent a scale for testing.

Your product supplier will likely know how heavy a full tank is, and maybe an empty one too.

If your tanks are currently placed on a stand of some sort, maybe putting some strain gauges on the stand would turn them into a scale.

The strain guages measure the deflection of the stand, which is proportional to the weight.

I have also seen tanks for several thousand gallons of product being weighed by using a balance. They rest on a platform (or stand) mounted on a long bar. The pivot point is close to the tank and the bar extends several feet past the pivot. Then enough standard weights are hung on the far end of the bar to balance the tank weight. Think 'balance scale'.

Cheers,
Tom
 
  • Sad
Likes Leopold89
  • #79
Juanda said:
Is this the diagram you're referring to?
Yes.
Juanda said:
It says R744 but that should be the same as CO2.
View attachment 350443

I'm still unsure of how to solve it but, from what you said, I can find the starting point from the known pressure and temperature.
Once it starts discharging, follow the isentropic line reducing pressure up to the new point read by the sensor.
Once the tank contents reaches the 2 phase region, the behavior of the contents remaining in the tank is no longer isentropic. This is because pure vapor is being removed from the valve, rather than a representative mixture of liquid and vapor from the tank. That was the idea of the analysis I did.
 
  • Informative
Likes Juanda
  • #80
Juanda said:
Is this the diagram you're referring to? It says R744 but that should be the same as CO2.
View attachment 350443

I'm still unsure of how to solve it but, from what you said, I can find the starting point from the known pressure and temperature.
Once it starts discharging, follow the isentropic line reducing pressure up to the new point read by the sensor.
From that, it'd be possible to know the vapor quality right after the release so we can find the portion of liquid and volume inside the tank. The densities of both are known since it's saturated and pressure and temperature are known so it's possible to calculate the mass.
During that process, I think I'd ignore the temperature sensor. We're already assuming it's got the same entropy so the temperature data is not necessary. My reason for ignoring it is its readings are indirect. I'd trust it when it has enough time to reach thermal equilibrium but not in cases like this.

View attachment 350445

We assumed an isentropic expansion which I believe also means it's adiabatic. It can be assumed that the heat input is not too significant during the discharging process. However, once the release is done and time passes, the tank will keep absorbing heat from the room because it's significantly cooler. As it absorbs heat, all the content will be transformed into gas now that the pressure is lower so it'd be possible to calculate the mass again but using the compressibility factor as mentioned in the previous post.

All this process (this and the procedure from post #73) completely bypasses the need for differential equations. However, to calculate everything in your Python code from your sensor data you'd need to add the functions from the diagrams we used. That is:
  • ##Z(P, T)##
  • All the properties that define the p-h diagram with the isolines.




PS.
In a closed system with a known mixture of saturated liquid and vapor at a known fixed volume, if I add a known amount of heat, what process does the mixture undergo? It can't be isochoric because, for that to happen, the system would need to be 100% vapor from the start. This detail is irrelevant for this exercise because sensors are available to obtain the necessary data (P and T). However, could I determine the path the mixture follows within the bell curve while heat is added at a fixed volume?
Perhaps I should start a new discussion to explore this point in more detail, rather than complicating this one further.
I used the Python package gascompressibility for the compressibility factor and got ~0.61 for the initial values. I calculated the spezific enthropy based on the initial values and then assumed it stayed constant and then calculated the temperature based on pressure and spezific enthropy, all using pyromat. From this calculated temperature I computed the compressibility and used the formula in post #73. In the below plots you see the result in the bottom right corner. Bottom left is the ODE solution with different initial mass based on my 0.61 compressibility. Changing the initial mass manually to the ~7.6kg does not change the shape and just shifts the plot down. The middle graph on the right is based on this. The middle graph on the left is the calculated temperature with constant spezific enthropy. I also checked for the liquid phase with interpolation and can confirm that in this data set no liquid is formed.
We can see that the temperature based on constant spezific enthropy is larger than room temperature T_T
Python code for temperature calculation:
pyromat.config['unit_pressure']='Pa'
pyromat.config['unit_temperature']='K'
co2 = pyromat.get('ig.CO2')
isentropicTemperature = co2.T(p=pressureData, s=co2.s(T=temperatureData[0], p=pressureData[0]))
 

Attachments

  • IsentropicApproach.png
    IsentropicApproach.png
    28.8 KB · Views: 13
Last edited:
  • #81
Leopold89 said:
I used the Python package gascompressibility for the compressibility factor and got ~0.61 for the initial values.
I guess that small difference is acceptable. I was eyeballing the values from the diagram. Python takes the exact values from the data.

Leopold89 said:
I calculated the spezific enthropy based on the initial values and then assumed it stayed constant and then calculated the temperature based on pressure and spezific enthropy, all using pyromat.
Did you need to calculate the entropy? I would have expected that value to be taken from a table. Or taken from a Python library just like ##Z##.

Secondly, did you read Chester's input on #79? What I did to describe the expansion of the content of the tank is only valid when we're outside the bell and it seems that you'd go into it during the isentropic expansion.
Chestermiller said:
Once the tank contents reaches the 2 phase region, the behavior of the contents remaining in the tank is no longer isentropic. This is because pure vapor is being removed from the valve, rather than a representative mixture of liquid and vapor from the tank. That was the idea of the analysis I did.

However, I still think you'd be able to calculate the mass as thermal equilibrium is reached as explained in #77.
Juanda said:
We assumed an isentropic expansion which I believe also means it's adiabatic. It can be assumed that the heat input is not too significant during the discharging process. However, once the release is done and time passes, the tank will keep absorbing heat from the room because it's significantly cooler. As it absorbs heat, all the content will be transformed into gas now that the pressure is lower so it'd be possible to calculate the mass again but using the compressibility factor as mentioned in the previous post.
From the equations I proposed you may not be able to calculate the mass when it's inside the bell as explained by Chester. You'd need the equations he proposed before but I don't understand them yet. I need to invest more time in them.

However, as soon as the content fully evaporates (which should do so as it absorbs heat from the ambient), you can repeat the process you did to find the initial mass when it was all vapor.

In summary, assuming that
  • you're mainly interested in the mass content of the tank and
  • the elapsed time between discharges is long enough for the content to evaporate completely
you could ditch the differential equations and calculate the mass of the tank using the compressibility factor from the readings produced by your sensors. The most accurate mass readings would be done right before the discharges since that's when the tank has had the most time to achieve thermal equilibrium so you can be sure the content is 100% vapor.
You wouldn't be able to have nice plots regarding how the mass content varies continuously with time but that data is irrelevant in the first place, right?
Your mass diagram would look something like this:
1724760590695.png


  1. The initial point we have established is known and it's all gas.
  2. Assuming 1 was already in thermal equilibrium with the room, from 1→2 nothing happens. If it wasn't in thermal equilibrium, we could have calculated the mass at 2 where, since enough time to evaporate completely has elapsed, the compressibility can be used. Since mass is conserved between 1 and 2, the mass is the same.
  3. The process 2→3 is a discharge. Hard stuff to do. At 3 there most likely is a mix of liquid and vapor within the tank due to the cooling effect of the isentropic expansion. I'll choose to ignore that process because I don't have the knowledge to tackle it yet. The important point to notice is from 3→4 the mass is preserved. Remember, mass is what we care about due to how the problem was originally defined and reiterated during the thread.
  4. The mass can be calculated at 4 because enough time has passed for the tank to reach thermal equilibrium at room temperature. Or at least, it has absorbed enough heat to completely evaporate its content.
  5. The process 4→5 is an intake. Since we have established the content at 4 is all gas, I believe this process can be assumed to be an isentropic compression of the previous content. However, that's not relevant to calculate the mass which is what we care about. At 5, the content should all be gas so you can use the previous equation again.
  6. The valves are closed from 5→6 so mass is preserved. Still, you can calculate the mass again to compare it with 5 because it's all gas just in case to check your numbers.
Remember, what I'm proposing should work because of the starting conditions you mentioned about your CO2 tank. Even in its historical peak pressure, at thermal equilibrium with the room, it's all gas. As long as you're below that, this method could be a good enough approximation for the mass content of the tank.
 
  • #82
Juanda said:
Did you need to calculate the entropy? I would have expected that value to be taken from a table. Or taken from a Python library just like Z.
As I wrote, I used the pyromat library for this.

Juanda said:
Secondly, did you read Chester's input on #79? What I did to describe the expansion of the content of the tank is only valid when we're outside the bell and it seems that you'd go into it during the isentropic expansion.
For the bell check I used this CO##_2## table attached here and interpolated it with
scipy.interpolate. This way I get a pressure for a given temperature and can check if the measurements are below this limit. If not the program aborts.
 

Attachments

  • CO2_table.txt
    1.2 KB · Views: 10
  • #83
Juanda said:
The most accurate mass readings would be done right before the discharges since that's when the tank has had the most time to achieve thermal equilibrium so you can be sure the content is 100% vapor.
That is the problem. I tried to find the time frames, where no discharge was happening by subtracting atmospheric pressure and gas pressure, but I did not get time frames with constant pressure.
 
  • #84
Leopold89 said:
As I wrote, I used the pyromat library for this.
All right. You said calculate so I thought you were getting the value through other means.

Leopold89 said:
For the bell check I used this CO##_2## table attached here and interpolated it with
scipy.interpolate. This way I get a pressure for a given temperature and can check if the measurements are below this limit. If not the program aborts.
I tried plotting some values from the document you provided and it seems accurate when I compare it to plots from the internet.
1724763901967.png


1724764011196.png


Where did you get that table?


Leopold89 said:
This way I get a pressure for a given temperature and can check if the measurements are below this limit. If not the program aborts.
Your sensors are reading the pressure inside the tank. Why are you trusting the temperature sensors in each time step instead? Due to your set up, they are indirect measurements of the tank.
I would rather trust the pressure sensors and only trust the temperature sensors when enough time has passed between discharges.
Also, are you saying your code doesn't register you're going inside the bell? I find it surprising because the initial conditions were very close to the bell but if you're always outside it makes the calculations simpler. You can use ##Z## all the way.
 

Attachments

  • 1724763861517.png
    1724763861517.png
    5.8 KB · Views: 16
  • #85
Leopold89 said:
That is the problem. I tried to find the time frames, where no discharge was happening by subtracting atmospheric pressure and gas pressure, but I did not get time frames with constant pressure.
How is the valve actuated? If it's electronically actuated, you can add the information to the time history.
Otherwise, even if you're not seeing when pressure is constant, you're seeing sudden peaks in pressure which should correspond to the charges and discharges. Try calculating the derivative ##\frac{dP}{dt}## and when it goes beyond a certain threshold you can assume it's a discharge/charge process. You'll have to tweak that threshold for both processes but it might work. Hopefully, the changes introduced by the valves admitting or outputting gas will be faster than the changes introduced by the system trying to reach equilibrium once the valves are closed.

If you're not seeing time intervals where pressure and temperature are constant it means the content has not had enough time to reach equilibrium. That's all right because we don't need it to be in equilibrium for our calculations, we just need it to be 100% gas.

I think the main thing you're missing is a reliable temperature sensor inside the tank.
 
  • #86
When you have 2 phases present inside, knowledge of the inside pressure tells you the inside temperature since the heat transfer is slow and the contents are thus very close to thermodynamic equilibrium. You can then determine the mass of CO2 in the tank using the saturated CO2 tables which give the liquid and vapor specific volumes at each saturation condition.

I have trouble understanding how the mass of CO2 inside the tank can increase if the valve only discharges mass.
 
  • #87
Juanda said:
Why are you trusting the temperature sensors in each time step instead?
First we assumed that the temperatures will get in an equilibrium fast and now with the isentropic approach I used calculated temperature values.
Juanda said:
How is the valve actuated? If it's electronically actuated, you can add the information to the time history.
Otherwise, even if you're not seeing when pressure is constant, you're seeing sudden peaks in pressure which should correspond to the charges and discharges. Try calculating the derivative ##\frac{dP}{dt}## and when it goes beyond a certain threshold you can assume it's a discharge/charge process. You'll have to tweak that threshold for both processes but it might work. Hopefully, the changes introduced by the valves admitting or outputting gas will be faster than the changes introduced by the system trying to reach equilibrium once the valves are closed.

If you're not seeing time intervals where pressure and temperature are constant it means the content has not had enough time to reach equilibrium. That's all right because we don't need it to be in equilibrium for our calculations, we just need it to be 100% gas.

I think the main thing you're missing is a reliable temperature sensor inside the tank.
The valve is actuated mechanically. And the sudden, small peaks do not correspond to discharges, because we only discharge around two times a day, I just learned. (BTW the tanks do not get charged only discharged)
I also asked the manufacturer and it is not possible to get a temperature sensor inside the tank.
Edit:
"Where did you get that table?"
I copied it from an old German textbook.
 
  • #88
Chestermiller said:
When you have 2 phases present inside, knowledge of the inside pressure tells you the inside temperature since the heat transfer is slow and the contents are thus very close to thermodynamic equilibrium. You can then determine the mass of CO2 in the tank using the saturated CO2 tables which give the liquid and vapor specific volumes at each saturation condition.

I have trouble understanding how the mass of CO2 inside the tank can increase if the valve only discharges mass.
Maybe some implementation error. I post my code, too:
Van-der-Waals-equation:
def vanDerWaals_a(inGas):
    return 27/64.0*math.pow(constants.R*criticalTemperature[inGas], 2)/criticalPressure[inGas]

def vanDerWaals_b(inGas):
    return constants.R*criticalTemperature[inGas]/8.0/criticalPressure[inGas]

def solveCubic(a, b, c, d):
    delta0 = b*b-3*a*c
    delta1 = 2*math.pow(b, 3)-9*a*b*c+27*a*a*d
    if delta0 == 0 and delta1 == 0:
        return -b/3.0/a
    if delta0 == 0:
        tempResult = delta1**(1/3.0)
    else:
        tempResult = ((delta1+cmath.sqrt(delta1*delta1 - 4*math.pow(delta0,3)))/2.0)**(1/3.0)
    rotAngle = (-1+cmath.sqrt(-3))/2.0
    result = []
    for i in range(0,2):
        result.append(-1/3.0/a*(b+(rotAngle**i) *tempResult + delta0/(rotAngle**i)/tempResult))
    return result

def calcParticles(tData, pData, fileName, volume):
    n_samples = len(tData)
    gasData=readSteamTable(fileName)
    pLimit = pchip_interpolate(gasData.t, gasData.p, tData)*bar
    adjustedN=np.zeros(n_samples)
    a = vanDerWaals_a(Gas.CO2)
    b = vanDerWaals_b(Gas.CO2)
    for i in range(0,n_samples):
        if pData[i]<pLimit[i]:
            tempResult = solveCubic(1, -volume/b, (pData[i]+constants.R*tData[i]/b)*volume*volume/a, -pData[i]*math.pow(volume, 3)/a/b)
            if checkRoots(tempResult):
                for root in tempResult:
                    if abs(root.imag)<1e-12 and root.real>0:
                        adjustedN[i]=root
            else:
                sys.exit()
        else:
            sys.exit()
    return adjustedN
This result is multiplied with ##0.0440098\frac{\mathrm{kg}}{\mathrm{mol}}##.

Compressibility Approach:
def CalcCompressibility(tData, pData):
    z=[]
    gasData=readSteamTable("CO2_table.txt")
    pLimit = pchip_interpolate(gasData.t, gasData.p, tData)*bar
    for i in range(0,len(tData)):
        if pData[i]<pLimit[i]:
            z.append(gc.calc_z(T=kelvinToFahrenheit(tData[i]), P=pData[i]*0.0001450377, Tr=tData[i]/criticalTemperature[Gas.CO2], Pr=pData[i]/criticalPressure[Gas.CO2]))
        else:
            sys.exit()
    return z
   
isentropicTemperature = co2.T(p=pressureData, s=co2.s(T=temperatureData[0], p=pressureData[0]))
masses = []
for i in range(0,len(z)):
    masses.append(volume/(z[i]*constants.R*isentropicTemperature[i]/pressureData[i])*0.0440098)

And lastly:
ODE:
def massDerivative(y, t, temperature, temperatureDot, pressure, hData, qData, qDotData, timeData):
    pyromat.config['unit_pressure']='Pa'
    pyromat.config['unit_temperature']='K'
    co2 = pyromat.get('ig.CO2')
    index = 0
    for i in range(0,len(timeData)):
        if t<timeData[i]:
            index = i
            break
    specHeatCap = co2.cv(T=temperature[index], p=pressure[index])
    nom = (qDotData[index] - specHeatCap*temperatureDot[index])*y
    denom = specHeatCap * temperature[index] - hData[index] - qData[index]
    result = nom/denom
    return result

uData = co2.e(T=temperatureData, p=pressureData)
hData = co2.h(T=temperatureData, p=pressureData)
timeData = np.linspace(0, 2*(len(temperatureData)-1), len(temperatureData))
hOutData = []
qData = []
for i in range(1, len(uData)):
    qData.append(uData[i-1] - uData[i])
    hOutData.append(hData[i-1] - hData[i])
qDotData = np.gradient(qData, timeData[1:])
tDotData = np.gradient(temperatureData, timeData)
initialMass=0.0440098*volume/(z[1]*constants.R*isentropicTemperature[1]/pressureData[1])
sol = odeint(massDerivative, initialMass, timeData[1:], tfirst = False, args=(temperatureData[1:], tDotData[1:], pressureData[1:], hOutData, qData, qDotData, timeData[1:]))
Here the z[1] is the same as in the previous code block.

Edit: I forgot to use the isentropic temperature in the ODE approach but now attached the version using calculated temperature.
 

Attachments

  • IsentropicApproach2.png
    IsentropicApproach2.png
    23.5 KB · Views: 12
  • #89
Just to be clear, when you post these graphs, temperature and pressure are calculated from initial conditions or are they the values straight from your sensors?
1724789265259.png

(By the way, you'd be able to copy-paste pictures instead of attaching them if that's more comfortable for you)

You mentioned you have sensors for pressure and temperature. (Even if the temperature is not really from inside the tank, we can address that later)
Can you upload those graphs if you haven't done it already? Because I really can't understand how it's possible that, even if you're ONLY discharging from the tank, we're seeing how pressure rises. Nor I can understand where the jagginess of the curve comes from. I'd expect sudden changes when the valve is open and the smooth behavior towards the new equilibrium point. Could it be electrical noise?
How long does the discharge take? Could you plot the temperature and pressure readings for a couple of days or so? It's to try to guess where are those 4 discharges (2 discharges/day) that you mentioned.
Can you add units to the horizontal axis?
 
  • #90
Juanda said:
Just to be clear, when you post these graphs, temperature and pressure are calculated from initial conditions or are they the values straight from your sensors?
View attachment 350481
(By the way, you'd be able to copy-paste pictures instead of attaching them if that's more comfortable for you)

You mentioned you have sensors for pressure and temperature. (Even if the temperature is not really from inside the tank, we can address that later)
Can you upload those graphs if you haven't done it already? Because I really can't understand how it's possible that, even if you're ONLY discharging from the tank, we're seeing how pressure rises. Nor I can understand where the jagginess of the curve comes from. I'd expect sudden changes when the valve is open and the smooth behavior towards the new equilibrium point. Could it be electrical noise?
How long does the discharge take? Could you plot the temperature and pressure readings for a couple of days or so? It's to try to guess where are those 4 discharges (2 discharges/day) that you mentioned.
Can you add units to the horizontal axis?
IsentropicApproach3.png

Sorry about the x-axis. I wrote that measurements take place every 2 minutes, so I got lazy and omitted the x-axis. The first row is direct sensor data for temperature and pressure. The x axis is the same for all plots, which is why I now only wrote it at the bottom.

The jagginess is only observable in CO##_2## and not in any other gas, but you see the same jagginess in all two CO##_2## tanks. That is why I think a defect or noise is unlikely, but on the other hand the other gases do not show this pattern. The slow rising pressure is, as far as I understand, the result of weather. Guessing the discharges might be difficult, because small amounts of this one gas are discharged.
The jagginess might also be only of secondary concern, because if the mass is calculated correctly, you should be able to smoothen this pattern away.
 
Last edited:
  • #91
Leopold89 said:
View attachment 350488
Sorry about the x-axis. I wrote that measurements take place every 2 minutes, so I got lazy and omitted the x-axis. The first row is direct sensor data for temperature and pressure. The x axis is the same for all plots, which is why I now only wrote it at the bottom.

The jagginess is only observable in CO##_2## and not in any other gas, but you see the same jagginess in all two CO##_2## tanks. That is why I think a defect or noise is unlikely, but on the other hand the other gases do not show this pattern. The slow rising pressure is, as far as I understand, the result of weather. Guessing the discharges might be difficult, because small amounts of this one gas are discharged.
The jagginess might also be only of secondary concern, because if the mass is calculated correctly, you should be able to smoothen this pattern away.

OK. So the long-term trends could be influenced by the weather. Would it be possible to plot this with a timestamp (e.g., day, hour, minute, or just day and hour)?

It would be feasible if you know the time at each time step, or you could assume the peak temperature represents the hottest time of day to see if that provides a clear pattern.

It might also help to understand how heat is transferred to the tank. For instance, I'd expect the tank temperature to rise around 15:00 when the room temperature is highest.

In addition to the temperature sensor you've mentioned, do you have another one to monitor the room from which the tank is absorbing heat?

However, those sudden pressure increments don't make sense. I can't figure out what's causing them. I could understand sudden drops in pressure if you're opening the valves to release pressure, but not the opposite. And even that should not cause such jaggedness since you mentioned that the valves only open a couple of times a day.

Can you find out how long the valves remain open? Is it a matter of seconds, minutes, or hours? You said they release small amounts, but I want to see if these releases could overlap with the long-term trends we're attributing to the weather.

Finally, can you share your pressure and temperature (P and T) data from the last plot in the same format you previously used to share the saturation bell for CO2? Ideally, with the timestamp included as well.
 
  • #92
Leopold89 said:
View attachment 350488
Sorry about the x-axis. I wrote that measurements take place every 2 minutes, so I got lazy and omitted the x-axis. The first row is direct sensor data for temperature and pressure. The x axis is the same for all plots, which is why I now only wrote it at the bottom.

The jagginess is only observable in CO##_2## and not in any other gas, but you see the same jagginess in all two CO##_2## tanks. That is why I think a defect or noise is unlikely, but on the other hand the other gases do not show this pattern. The slow rising pressure is, as far as I understand, the result of weather. Guessing the discharges might be difficult, because small amounts of this one gas are discharged.
The jagginess might also be only of secondary concern, because if the mass is calculated correctly, you should be able to smoothen this pattern away.
How can the mass increase if the tank is only vented.
 
  • Like
Likes Vanadium 50 and Juanda
  • #93
Chestermiller said:
How can the mass increase if the tank is only vented.
Here is the kind of pressure regulator I am using to vent gas, but in German. Mass should therefore not flow inside. I would not think mass is increasing, but instead that too many simplifications were made or a miscalculation happened somewhere, which is why I posted my code.

If mass increases, ##\Delta m \propto \frac{p_1}{T_1}-\frac{p_2}{T_2}##, I would guess that either the pressure ##p_2## is smaller than it should be or the temperature ##T_2## is larger than it should be. The last case makes sense and for me it means that the heat transfer from room to gas takes time that is not negligible.

Another one of my fruitless ideas is to plot a histogram of enthropy or enthalpy. After each release the peak should move further away and this new expectation value could be used to determine the system variables in equilibrium, but the enthalpy curve looks just like the temperature curve.
 
  • #94
Leopold89 said:
Here is the kind of pressure regulator I am using to vent gas, but in German. Mass should therefore not flow inside. I would not think mass is increasing, but instead that too many simplifications were made or a miscalculation happened somewhere, which is why I posted my code.

If mass increases, ##\Delta m \propto \frac{p_1}{T_1}-\frac{p_2}{T_2}##, I would guess that either the pressure ##p_2## is smaller than it should be or the temperature ##T_2## is larger than it should be. The last case makes sense and for me it means that the heat transfer from room to gas takes time that is not negligible.

Another one of my fruitless ideas is to plot a histogram of enthropy or enthalpy. After each release the peak should move further away and this new expectation value could be used to determine the system variables in equilibrium, but the enthalpy curve looks just like the temperature curve.

With that kind of valve, the mass flow should only be towards the exit (only discharges happening).
And it should only depend on the forces ##F_1## and ##F_2## which are defined by the difference in pressure between the chambers (inside the tank and room pressure for simplification) and the springs installed.
1724847507400.png


In summary, once the pressure tank goes beyond a certain pressure limit defined by the ambient pressure and the springs, the valve will open.

In this case, since the input energy that could cause the valve opening simply comes from the heat in the room, I believe the released mass will be very small until pressure equilibrium is reached again.
The content of the tank might not even go inside the bell because of how small is the release. As you suggested in a previous post your program is not aborting the calculation so the content seems to be 100% gas all the time.
Leopold89 said:
For the bell check I used this CO##_2## table attached here and interpolated it with scipy.interpolate. This way I get a pressure for a given temperature and can check if the measurements are below this limit. If not the program aborts.

So you might release some mass at the beginning when the tank is full but you won't release any mass in future days because the heat input from the room won't be enough to increase the pressure to the point to force the valve opening again now that there is less mass inside. This is unless the next day is significantly hotter than the previous day.

Again, if you supply your temperature and pressure data I'd like to attempt to calculate the mass myself only using the compressibility factor ##Z##.
Assuming that the content doesn't turn to liquid, you don't need differential equations to know the mass at any moment. The procedure established in#73 should suffice. I don't understand why you're using Van-der-Waals-equation and ODEs.
And even if it does transform to liquid during discharges, you could calculate the mass assuming it doesn't. You might get weird values in the time steps where there is liquid in the chamber but that liquid should turn into gas soon so you should see periods of time where the mass is constant which correspond to those times where there is 100% gas in the tank and the valves are closed. However, your pressure and temperature sensors must give you reliable data for that calculation to be accurate.
 
  • #95
Let’s see what you get if you plot the measured outside temperature at time t as a function of the measured inside pressure at time t.
 
  • #96
Juanda said:
Would it be possible to plot this with a timestamp (e.g., day, hour, minute, or just day and hour)?
data2.png

Juanda said:
It might also help to understand how heat is transferred to the tank. For instance, I'd expect the tank temperature to rise around 15:00 when the room temperature is highest.
In above plot I believe to see how the temperature is maximal between 12:00 and 16:00, but the pressure is maximal at around 16:00, maybe a little later than the temperature.
Juanda said:
In addition to the temperature sensor you've mentioned, do you have another one to monitor the room from which the tank is absorbing heat?
Do you mean the room that affects the pressure regulator? I don't think the cable is long enough to get the second one there.
Juanda said:
Finally, can you share your pressure and temperature (P and T) data from the last plot in the same format you previously used to share the saturation bell for CO2? Ideally, with the timestamp included as well.
pt3d.png

Did you mean something like this? The z-axis is still time in minutes though.
Chestermiller said:
Let’s see what you get if you plot the measured outside temperature at time t as a function of the measured inside pressure at time t.
pTplot.png

I switched the axes, because it fits better with the phase diagrams I know. Orange is the interpolated vapour-liquid-limit from my table.

Juanda said:
Again, if you supply your temperature and pressure data I'd like to attempt to calculate the mass myself only using the compressibility factor Z.
Oh, I thought you meant plots, not a data file. Attached here.
 

Attachments

  • SmallDataSet.txt
    45.8 KB · Views: 14
  • #97
Leopold89 said:
For the bell check I used this CO##_2## table attached (...)
What's the meaning of ##r##?
1724867408582.png


I think I don't need it for the way I want to solve it but I'd like to know.
 
  • #98
Leopold89 said:
View attachment 350501

In above plot I believe to see how the temperature is maximal between 12:00 and 16:00, but the pressure is maximal at around 16:00, maybe a little later than the temperature.

Do you mean the room that affects the pressure regulator? I don't think the cable is long enough to get the second one there.

View attachment 350504
Did you mean something like this? The z-axis is still time in minutes though.

View attachment 350502
I switched the axes, because it fits better with the phase diagrams I know. Orange is the interpolated vapour-liquid-limit from my table.
This graph seems to suggest that, since the pressure is less than the equilibrium vapor pressure at each temperature, the gas is always superheated. That would mean that there is a single phase at all times. (I assume you have plotted the absolute pressures, not the gauge pressures, which are 0.1 MPa lower). Under these circumstances, the equation of state indicated by Juanda applies: $$\frac{m}{V}=\frac{PM}{zRT}$$where m is in grams, and R=8.314 J/mole-K. So you can calculate how m varies with time in your example (assuming the temperature in the tank has equilibrated with the outside temperature at each time). What do you calculate?
 
  • #99
Chestermiller said:
I assume you have plotted the absolute pressures, not the gauge pressures, which are 0.1 MPa lower
Oh, no. I plotted the gauge pressures. The absolute pressures are only in post #71.
Chestermiller said:
What do you calculate?
I calculate precisely this in post #88. It is the bottom right graph.
 
  • #100
Leopold89 said:
Oh, no. I plotted the gauge pressures. The absolute pressures are only in post #71.
:(
That could change the problem significantly. I did everything assuming we're talking about absolute pressures.

Leopold89 said:
I calculate precisely this in post #88. It is the bottom right graph.
If that's true, then all I can think of is two things:
  • either the new data regarding absolute pressures changes the problem significantly because you're inside the bell although you mentioned you're checking if that's the case and it seems it's gas all the time
  • or your sensors are not accurate.
My bet is that your temperature sensor is not accurate to describe the gas' temperature since it's not inside the tank. Could you at least attach it to an outside wall of the tank and cover it with an insulating paste of some kind?
However, it's also true that the jaggedness is introduced by the pressure sensor... I really don't know what's going on and I'm running out of ideas.
Maybe some sort of resonance between the springs in the valve and the compressed gas? I'd be very surprised it's not dissipated quickly and also the period is too long. I'm throwing ideas to the wall and seeing what sticks at this point.

By the way, I didn't know you already tried my approach on post #88 so I did it too although I'm using the values from #73 where I assumed we're talking about absolute pressures.
In theory, the problem should be doable just with Pyromat because you can get the specific volume from the pressure and temperature but I got a different result compared to what was done in #73. I guess I'm not experienced enough with that library and I'm committing a mistake. I calculated the mass again using the gascompressibility library to find ##Z## (after a few awful unit conversions) and then I did get the same results as in post #73.
1724879077859.png


So the red one is the one I trust for now. I can't find what I did wrong with Pyromat because I told it to use the SI units.

I won't be able to invest more time in this for a few days. And even if I did I feel I have nothing else to add. I'll keep my eyes open in case the mysteries are solved somehow. Again, I think you'd get some actual temperature readings from inside the tank if you want to proceed with this method. If the manufacturer insists on it not being possible then you'll have to fight him or find an alternative method. Maybe even checking your pressure sensors to see if they're working correctly.

Here is the associated code and text file containing the pressure and temperature history.
NOTE: I once tried to copy code from the Forum to Spyder and it didn't work very well. If you have that issue, try pasting it on ChatGPT and it'll give you the code back in a format you can easily paste on Spyder.
MassCalculation:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import pyromat as pm
import gascompressibility as gc

pm.config['unit_pressure']='Pa'
pm.config['unit_temperature']='K'
pm.config['unit_mass']='kg'
pm.config['unit_volume']='m3'


co2 = pm.get('ig.CO2')
V = 0.05 # Tank's volume [m3]

# Specify the path to the file
# Pressure and temperature at a given time step by the sensors
file_path_1 = 'D:\Datos de Usuario\Descargas\SmallDataSet_Juanda.txt'
dataPTt = pd.read_csv(file_path_1, sep=';')

Time_sensor_array = dataPTt['Time [date, hour]'].to_numpy() #[date, hour]
Temperature_sensor_array = dataPTt['Temperature [K]'].to_numpy() #[K]
Pressure_sensor_array = dataPTt['Pressure [Pa]'].to_numpy() #[Pa]

def create_incrementing_array(input_array):
    # Measure the size of the input array
    size = len(input_array)
    
    # Create a new array starting from 0 and incrementing by 2 until it's the same size as the input array
    new_array = [i * 2 for i in range(size)]
    
    return new_array

Time_sensor_array_2min = create_incrementing_array(Time_sensor_array)

# Using pyromat to find the specific volume [m3/kg]. It gives of a different result than in post #73 using compressibility (I thought it was absolute pressures)
Specific_volume_pyromat = co2.v(T=Temperature_sensor_array, p=Pressure_sensor_array) #[m3/kg]
# print(Specific_volume_pyromat[0])
m_pyromat = V/Specific_volume_pyromat


P_cr_CO2 = 7.39*10**6 # Critical pressure (top of the bell) [Pa]
T_cr_CO2 = 304.2 # Critical temperature (top of the bell) [K]
R = 188.9 # Gas constant [J/kgK]

P_R_CO2 = Pressure_sensor_array/P_cr_CO2
T_R_CO2 = Temperature_sensor_array/T_cr_CO2

Pressure_sensor_array_PSGI = (Pressure_sensor_array-(0.1*10**6))/6895
Temperature_sensor_array_F = (Temperature_sensor_array - 273.15) * 9 / 5 + 32

Z_0 = gc.calc_z(sg=1, P=Pressure_sensor_array_PSGI[0], T=Temperature_sensor_array_F[0], H2S=None, CO2=1, N2=None, Pr=P_R_CO2[0], Tr=T_R_CO2[0], pmodel='piper')
# print(Z_0)

def compute_compressibility_factors(pressure_array, temperature_array, Pr_array, Tr_array):
    """
    Compute the compressibility factor Z for each coordinate in the input vectors.

    Parameters:
    - pressure_array: Array of pressure values in PSGI.
    - temperature_array: Array of temperature values in Fahrenheit.
    - Pr_array: Array of reduced pressures for each coordinate.
    - Tr_array: Array of reduced temperatures for each coordinate.

     Returns:
    - A NumPy array of compressibility factors Z.
    """
    # Compute Z for each coordinate and collect the results in a list
    Z_list = [gc.calc_z(
        sg=1,
        P=pressure_array[i],
        T=temperature_array[i],
        H2S=None,
        CO2=1,
        N2=None,
        Pr=Pr_array[i],
        Tr=Tr_array[i],
        pmodel='piper'
    ) for i in range(len(pressure_array))]

    # Convert the list of Z values to a NumPy array
    return np.array(Z_list)


Z_values = compute_compressibility_factors(
    Pressure_sensor_array_PSGI,
    Temperature_sensor_array_F,
    P_R_CO2,
    T_R_CO2
)

# print(Z_values[0])


# Using compressibility (import gascompressibility as gc) to find the specific volume [m3/kg]. I get the same as in #73 (assuming absolute pressures)
Specific_volume_compressibility = (Z_values*R*Temperature_sensor_array)/Pressure_sensor_array #[m3/kg]
m_gascompressibility = V/Specific_volume_compressibility
# print(m_gascompressibility[0])


# Ensure Time_sensor_array_2min has the same length as m_pyromat and m_gascompressibility
# If Time_sensor_array_2min does not match the lengths of m_pyromat and m_gascompressibility, adjust accordingly.

# Create the plot
plt.figure(figsize=(12, 6))

# Plot m_pyromat
plt.plot(Time_sensor_array_2min, m_pyromat, label='m_pyromat', color='blue', marker='o')

# Plot m_gascompressibility
plt.plot(Time_sensor_array_2min, m_gascompressibility, label='m_gascompressibility', color='red', marker='x')

# Add labels and title
plt.xlabel('Time (min)')
plt.ylabel('Mass (kg)')
plt.title('Comparison of Mass Calculated with pyromat and gascompressibility')
plt.legend()
plt.grid(True)

# Display the plot
plt.show()
 

Attachments

  • SmallDataSet_Juanda.txt
    45.9 KB · Views: 13
  • #101
Leopold89 said:
Oh, no. I plotted the gauge pressures. The absolute pressures are only in post #71.

I calculate precisely this in post #88. It is the bottom right graph.
Well, obviously there is something wrong because the mass should never increase.
 
  • #102
Juanda said:
:(
That could change the problem significantly. I did everything assuming we're talking about absolute pressures.
Not really. The difference between absolute pressure and gauge pressure is only 0.1 MPa.
 
  • #103
Probably does not matter, but:
Juanda said:
In summary, once the pressure tank goes beyond a certain pressure limit defined by the ambient pressure and the springs, the valve will open.
Umm, not quite. 🫨
Juanda said:
1724847507400.png

It is designed to close when the tank being filled reaches a set pressure.

Note that the outlet pressure, not the source-tank pressure, acts on the diaphragm, closing the valve.

("absperrventil", on the right, is the "shut-off valve"
'Zum Brenner' translates as 'to the burner'.)

Cheers,
Tom
 
  • Informative
Likes Juanda
  • #104
Chestermiller said:
Well, obviously there is something wrong because the mass should never increase.
Juanda got increasing masses, too.

Juanda said:
What's the meaning of ##r##?
View attachment 350506

I think I don't need it for the way I want to solve it but I'd like to know.
These are the differences between the vapour and liquid enthalpies.
 
  • #105
Tom.G said:
The way this thread is going, it looks like that 'future' is rapidly approaching!

Since the tanks are from an outside source, try getting a scale for one of the tanks and have a tank delivered to it (or moved to it). Maybe you will have to make room somewhere, or put it in an inconvenient spot. Try it out to see how well it works.

You may even be able to rent a scale for testing.

Your product supplier will likely know how heavy a full tank is, and maybe an empty one too.

If your tanks are currently placed on a stand of some sort, maybe putting some strain gauges on the stand would turn them into a scale.

The strain guages measure the deflection of the stand, which is proportional to the weight.

I have also seen tanks for several thousand gallons of product being weighed by using a balance. They rest on a platform (or stand) mounted on a long bar. The pivot point is close to the tank and the bar extends several feet past the pivot. Then enough standard weights are hung on the far end of the bar to balance the tank weight. Think 'balance scale'.

Cheers,
Tom
Can anybody recommend a proper scale? One which I can connect to a (Linux) computer, can recalibrate myself and is cheap?
 

Similar threads

Back
Top