-
Notifications
You must be signed in to change notification settings - Fork 393
Standard and normal flow rates, SCFH and Nm^3/h #724
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
Comments
Hi there, thanks for doing all the research on the existing issues and creating a summary here. I don't know this domain at all and I currently don't what exactly is lacking in Units.NET or what needs to be added or changed. My time is also very limited these days with family and other projects, so if you could create a short bullet list of what concrete changes you'd like to see in Units.NET, or maybe you need some help from me to decide how to fit a new concept into Units.NET, then it's a lot easier for me to comment on it and help move this forward. Should we add some new quantities? New units? Should some quantities be renamed/repurposed because they are wrong/ambiguous? Should we add some wrapper types that help with conversions by adding extra information that our current quantities don't provide? Things like that. The more specific/concrete the better. Pseudocode examples of intended usage is also very nice. I see I commented in #713 that my understanding was that maybe a wrapper type with some more context/information is needed to solve this. Best, |
I suppose it's also worth noting, this applies only to gas flow rather than liquid flow.
I'm still trying to familiarize myself with the architecture of UnitsNet. I assume every conversion across quantities requires a wrapper? I would expect that conversions to a So I suppose I'm proposing creating instances of new StandardFlow(MassFlow massFlow, double gasSpecificGravity)
new StandardFlow(MassFlow massFlow, Density gasDensity) or from new StandardFlow(VolumeFlow volumeFlow, Temperature fluidTemperature, ReferencePressure fluidPressure, ReferencePressure saturationPressure, double relativeHumidity, ReferencePressure standardPressure, Temperature standardTemperature) We would see more varieties to the constructor from |
It depends on what you mean. The architecture is currently designed to use a code generator to read JSON files like Length.json and output C# code with quantity types like We sometimes extend the generated code to allow for converting to other quantities, such as Extensions like that are manually added to partial files like Mass.extra.cs and Length.extra.cs. So if I understood you right, there is typically no wrapper type involved in cross-quantity conversions, only partial files that add extra methods and members to the generated quantity types. So in your suggestion, if you are generating the public partial struct StandardFlow
{
/* extra methods, properties etc here */
} If you are NOT generating the code for I'm slowly getting a better grip on this, but it would still help a lot for my understanding if you could show a couple of concrete, real-life examples of how you would like to use this in your day to day work:
|
@angularsen, Thank you for the clarification of the architecture. Based on that, I suspect the best way to avoid code duplication would be to build upon
Because temperature, pressure, and fluid are held constant, a Standard, or Normal flow rate can be treated as
In my line of work:
As I continue in detail on this topic, I start to wonder the same question of scope you mentioned previously in #713. Considering at the highest level though, |
My head is exploding a bit on level of detail here :D It's great, just a lot to take in.
With These sound to me like wrapper types that add extra information about the volume flows and you can add methods to the wrappers to convert between compressible and incompressible, which would change the internal Change proposalLet's try to get a bit more concrete. I find that easier to discuss. Since a standard flow needs additional information beyond Add wrapper types to represent StandardFlow and NormalFlow
Questions:
Convert between MassFlow|VolumeFlow and StandardFlow|NormalFlowSee code example below. Parsing and ToString() of standard/normal flowsDo we need to support parsing strings like Some examples here: Without code generator, we need to manually add support for parsing these. As for ToString(), we could do it easy and add suffix "(standard)" or "(standard ISO)" behind the If we add Parse() support, then it should be able to parse whatever ToString() outputs. Example implementation for discussionA single wrapper type to hold information for Standard/Normal flow rates, with methods to convert to/from MassFlow and VolumeFlow. // Not sure about this name, trying find something that represents a flow at a certain condition, maybe FlowWithReferenceCondition or something
public abstract class ReferenceFlow
{
public ReferenceFlow(MassFlow mf, Pressure p, Temperature t, ReferenceFlowCondition c)
{
/* assign properties here */
}
public VolumeFlow VolumeFlow { get; }
public Pressure BasePressure { get; }
public Temperature BaseTemperature { get; }
// Arguably we don't need this if we use inheritance and separate types for each condition and can match on type instead, but I like having both
public ReferenceFlowCondition ReferenceFlowCondition { get; }
public MassFlow ToMassFlow() {}
public VolumeFlow ToVolumeFlow() {}
}
public enum ReferenceFlowCondition
{
StandardUS,
StandardISO,
StandardAGA,
Normal,
Custom
}
// Each standard as its own type, inheriting from base type
public class StandardFlowUS : ReferenceFlow
{
public StandardFlowUS(/*args*/) : this(/*pass on args to ReferenceFlow ctor*/) {}
}
public class StandardFlowISO : ReferenceFlow
{
public StandardFlowISO(/*args*/) : this(/*pass on args to ReferenceFlow ctor*/) {}
}
public class StandardFlowAGA : ReferenceFlow
{
public StandardFlowAGA(/*args*/) : this(/*pass on args to ReferenceFlow ctor*/) {}
}
public class NormalFlow : ReferenceFlow
{
public NormalFlow(/*args*/) : this(/*pass on args to ReferenceFlow ctor*/) {}
}
// The idea is to support custom conditions beyond the standard ones, I don't know if this will ever be useful though?
public class CustomReferenceFlow : ReferenceFlow
{
public CustomReferenceFlow(/*args*/) : this(/*pass on args to ReferenceFlow ctor*/) {}
}
// MassFlow.extra.cs
public static StandardFlowUS ToStandardFlowUS(this MassFlow mf, /* other args */) {}
public static StandardFlowISO ToStandardFlowISO(this MassFlow mf, /* other args */) {}
public static StandardFlowAGA ToStandardFlowAGA(this MassFlow mf, /* other args */) {}
public static NormalFlow ToNormalFlow(this MassFlow mf, /* other args */) {}
// VolumeFlow.extra.cs
public static StandardFlowUS ToStandardFlowUS(this VolumeFlow vf, /* other args */) {}
public static StandardFlowISO ToStandardFlowISO(this VolumeFlow vf, /* other args */) {}
public static StandardFlowAGA ToStandardFlowAGA(this VolumeFlow vf, /* other args */) {}
public static NormalFlow ToNormalFlow(this VolumeFlow vf, /* other args */) {} Let me know what you think of this direction. |
Adding @bitbonk, @lipchev, @MarcDrexler to the discussion, since they have been involved in previous related issues. |
This has been a long stream of consciousness... I keep forgetting about the I now think its best to describe standard/normal flows as While thinking through this some more, I realize we can simplify this to two separate problems:
Standard Temperature and Pressure What if we created a Note: I realized when thinking about STP, that the development of A couple conversion thoughts:
Parsing and ToString() As we can see above, with I would suggest additionally including the fluid in the suffix when deviating from the standard specification, such as For example public enum StandardReference
{
StandardUS,
StandardISO,
StandardAGA,
Normal,
Custom
}
public struct EnvironmentalReference
{
public EnvironmentalReference(Pressure p, Temperature t, double RelativeHumidity)
{
/*Assign properties here*/
}
public static Dictionary<StandardReference, EnvironmentalReference> StandardReferences { get; } = new Dictionary<StandardReference, EnvironmentalReference>()
{
{StandardReference.StandardUS, new EnvironmentalReference(Pressure.FromPoundsForcePerSquareInch(14.696), Temperature.FromDegreesFahrenheit(60), 0)},
/*Define subsequent StandardReferences here*/
};
{
public struct ReferenceVolumeFlow
{
public ReferenceVolumeFlow(VolumeFlow q, EnvironmentalReference e, MolarMass m)
{
/*Assign properties here*/
/*Calculate density here*/
}
public ReferenceVolumeFlow(VolumeFlow q, Pressure p, Temperature t, MolarMass m)
{
/*Assign properties here*/
/*Calculate density here*/
}
public ReferenceVolumeFlow(VolumeFlow q, Pressure p, Temperature t, MolarMass m, CompressibilityFactor z)
{
/*Assign properties here*/
/*Calculate density here*/
}
public ReferenceVolumeFlow(VolumeFlow q, Density d)
{
/*Assign density here*/
}
public ReferenceVolumeFlow ToCondition (Pressure p, Temperature t)
{
/*pass on args to ReferenceVolumeFlow ctor*/
}
public ReferenceVolumeFlow ToCondition (EnvironmentalReference e)
{
/*pass on args to ReferenceVolumeFlow ctor*/
}
public ReferenceVolumeFlow ToFluid (MolarMass m, CompressibilityFactor z)
{
/*pass on args to ReferenceVolumeFlow ctor*/
}
public MassFlow ToMassFlow (MassFlowUnit u)
{
/*Calculate from properties*/
/*Pass on args to MassFlow ctor*/
}
/*Other overloads*/
{
//MassFlow.Extra.cs
public static ReferenceVolumeFlow ToReferenceVolumeFlow(this MassFlow mf, /* other args */) {}
//VolumeFlow.Extra.cs
public static ReferenceVolumeFlow ToReferenceVolumeFlow(this VolumeFlow vf, /* other args */) {} |
Not easy as of today, to my knowledge. I don't want to resort to manually keeping any hard coded delegate methods in the wrapper type in sync whenever conversion methods change in
This would be trivial to add with some extension methods that take the relevant arguments to convert between those two, as detailed earlier. To distinguish between ideal and real gases, I'd suggest including that in the name of the conversion methods, maybe like this
Yes, but parsing is mutually exclusive to each quantity type, so for
Good, that is easier to do.
Good suggestion, I like it.
OK, if that makes sense to your domain and usage, then sure. It would require specifying this when constructing the wrapper quantity type. Great example, some comments:
|
Just as a side note: While it is nice to have conversions built in between the different flow quantities, for us it would be enough just to have the standard flow quantity (including the unit sccm, displayable as "sccm"). We will hardly ever have the need for such conversions. I suspect that if the conversions between the different flow quantities is not built in, it would still be possible to do it manually, right? |
@bitbonk, Does that mean the only functionality you need is around parsing and ToString(), including shorthand strings like SCCM? The only problem I foresee with shorthand strings like SCCM, SCFM, SCFH, etc. is the ambiguity of STP for example, SCFM wiki. I haven't yet figured out a good way to avoid the ambiguity.
I like the suggestion for distinguishing between ideal and real conversion methods. I'm not sure if we would need to store information on ideal, real or liquid, since the relevant conversions would be to or from
That is a good point. I referenced it when discussing string parsing, and jumped to its properties, like More nitpicky on my end, but it slipped my mind that compressibility factor is actually a calculated value from gas For example public enum CommonFluid
{
DryNitrogen,
NaturalGas,
LiquidPropane,
DryAir,
Water
}
public struct Fluid
{
public Fluid(string Name, MolarMass m, Density d, ReferencePressure p, Temperature t, /*Any other relevant physical properties*/ )
{
/*Assign properties here*/
}
public static Dictionary<CommonFluid, Fluid> CommonFluid{ get; } = new Dictionary<CommonFluid, Fluid>()
{
{CommonFluid.DryNitrogen, new Fluid("Dry Nitrogen, MolarMass.FromGramPerMole(28.013), Density.FromGramPerLiter(1.126), new ReferencePressure(Pressure.FromPoundsForcePerSquareInch(14.696)), Temperature.FromDegreesFahrenheit(60))} ,
/*Define subsequent CommonFluids here*/
};
{
I agree this one needs to be addressed. I would think I think storage at that level deals with the issue I recognized in my last comment about
Would you be able to clarify the storage of those objects, and share your thoughts on this idea? |
I think I've mentioned this before, but I still wonder if the notion of a "reference scale" is not something that's fundamentally missing from our quantities. I guess you could put all of the conversion code in the *.extra.cs if you had access to the reference scale as part of the quantity. I mean we already have some implicit one dimensional scale: f(x)->x for [double.MinValue. double.MaxValue]. Give it some type/struct, a decent name and put as the default value for the construction of normal quantities- all arithmetic operations operating on the same scale are preserved- operations on different scales are either not supported or use some special mapping function from one scale to another. Custom scale classes are created per quantity, are strongly typed and hold whatever necessary information. We add a property to the json schema that associates the quantity with the special type- and the generator uses it to create a field of the class type. |
Hmm, here's one final thought(possibly out of scope): you know how we loose precision with units that are far away from the base unit- what if some basic quantity, like Length had multiple possible scales- e.g. Macro, Normal, Micro- and the scales corresponded to some mapping function like f(x)->10e6 * x that is applied on the conversion coefficient- then adding for instance two very small lengths with different units, but in the same scale should preserve some of the precision of the result. |
We would like to have the standard flow quantity that has parsing and ToString() for SCCM and that can convert between different units within that quantity (eg. from SCCM to SCM). Conversion to other quantitites is nice to have but not required. So I would say the answer to your question is yes. |
This is great! 😀 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
I created a PR for the simple version without complex conversions proposed by @bitbonk. |
First-time contributor here!
It looks like this feature idea has been addressed to some degree already in #212 and #713.
It is already clear that standard and normal flow rates are missing in UnitsNet. Units such as SCCM (standard cubic centimeters/min), SCFM (standard cubic feet/min), SCFH (standard cubic feet/hour), Nm3/h (normal cubic meters/hour), etc. are best described as the type MassFlowUnit rather than VolumeFlowUnit. This is due to the gas flow being constrained to Normal or Standard pressure and temperature conditions as previously mentioned in #212. As mentioned in #713, these units are typically used with mass flow meters and controllers.
Conversions from standard and normal flow rates to more conventional mass flow rates are then just constant conversions like any other with the exception that the specific gravity of the gas must also be accounted for.
For example, lb/h gas to SCFH gas:
SCFH gas = lb/h*13.1/S.G.
S.G.: Gas Specific Gravity (unitless)
Converting from mass flow in SCFH to volume flow in ACFH (actual cubic feet per hour) of course is more complex as has been discovered in #212 and #713.
How can I help?
Some references:
Fisher Control Valve Handbook Chapter 15: Conversions and Equivalents
Wikipedia: SCFH
The Engineering Toolbox: Gas Specific Gravity
The text was updated successfully, but these errors were encountered: