Conversation
- correct gas product volume equation - update grams when gas product moles change - update TON when catalyst mole value changes - recalculate dependent gas-product fields when vessel volume changes - refactor unit conversion tests for gas-phase scheme - add ReactionDetailsScheme unit tests for gas-phase reactions
There was a problem hiding this comment.
Pull request overview
This PR refactors gas-phase reaction calculations in the ReactionDetailsScheme component to improve synchronization and correctness of dependent field calculations. The changes focus on ensuring that gas product properties (volume, moles, grams, TON) are properly recalculated when vessel volume or catalyst mole values change.
Key changes:
- Corrected gas product volume calculation formula and updated related unit conversion logic
- Added automatic recalculation of gas product fields when vessel size changes
- Enhanced TON/TOF calculations to update when catalyst moles change
- Improved test coverage with comprehensive gas-phase reaction test suite
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
app/javascript/src/utilities/UnitsConversion.js |
Refactored calculateVolumeForFeedstockOrGas to accept vessel volume parameter; improved determineTONFrequencyValue with better input validation and documentation |
app/javascript/src/models/Sample.js |
Updated volume and gram conversions for gas-type samples to use vessel volume; added gas-specific gram calculation when amount unit is 'g' |
app/javascript/src/apps/mydb/elements/details/reactions/schemeTab/ReactionDetailsScheme.js |
Added vessel size change event handler to trigger gas product recalculations; updated TON values when catalyst amount changes; improved template state handling |
app/javascript/src/apps/mydb/elements/details/reactions/ReactionDetails.js |
Removed async wrapper from updateReactionVesselSize; added synchronous store updates for vessel size changes; initialized gas phase store on component mount and update |
app/javascript/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js |
Added zero-purity guard in getGramFromMol to prevent division by zero |
spec/javascripts/utils/UnitConversion.spec.js |
Updated test parameters for calculateVolumeForFeedstockOrGas to match refactored function signature |
spec/javascripts/packs/src/apps/mydb/elements/details/reactions/schemeTab/GasPhaseReactionDetailsScheme.spec.js |
Added comprehensive test suite covering feedstock, catalyst, and gas product calculations including edge cases |
spec/javascripts/packs/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsComponents.spec.js |
Updated volume assertion to verify it's defined rather than expecting proportional scaling |
spec/javascripts/helper/gasPhaseReactionTestHelpers.js |
New helper file providing factory functions for creating gas-phase test data |
spec/javascripts/helper/reactionVariationsHelpers.js |
Added molecule_molecular_weight property definition to prevent getter loss during deep cloning; updated gas phase test data with realistic values |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...cks/src/apps/mydb/elements/details/reactions/schemeTab/GasPhaseReactionDetailsScheme.spec.js
Show resolved
Hide resolved
| if (gasType === 'gas') { | ||
| molAmount = calculateGasMoles(vesselVolume, part_per_million, temperatureInKelvin); | ||
| return calculateGasVolume(molAmount, gasPhaseData); |
There was a problem hiding this comment.
[nitpick] There's a circular calculation issue in the calculateVolumeForFeedstockOrGas function for gas type. When gasType === 'gas', the function:
- Calculates moles from vessel volume using
calculateGasMoles(vesselVolume, part_per_million, temperatureInKelvin) - Then immediately calculates volume from those moles using
calculateGasVolume(molAmount, gasPhaseData)
This means for gas products, the function is calculating: volume = moles * R * T where moles = (ppm * vesselVolume) / (R * T * 1e6), which simplifies to volume = (ppm * vesselVolume) / 1e6. This appears to be calculating the actual volume of gas based on ppm concentration, not verifying the calculation round-trip.
According to the test comment on line 79-80, the expected behavior seems correct (calculating actual gas volume from ppm), but the function name calculateVolumeForFeedstockOrGas and the circular pattern may be confusing. Consider clarifying the purpose in documentation or renaming the function to better reflect that it's calculating the actual volume occupied by the gas based on ppm concentration.
There was a problem hiding this comment.
@copilot there is a difference between vesselVolume which is vessel size of the reaction, and volume of gas product!
app/javascript/src/apps/mydb/elements/details/reactions/schemeTab/ReactionDetailsScheme.js
Outdated
Show resolved
Hide resolved
...cks/src/apps/mydb/elements/details/reactions/schemeTab/GasPhaseReactionDetailsScheme.spec.js
Outdated
Show resolved
Hide resolved
...cks/src/apps/mydb/elements/details/reactions/schemeTab/GasPhaseReactionDetailsScheme.spec.js
Outdated
Show resolved
Hide resolved
| if gas_type == 'feedstock' && unit == 'l' | ||
| calculate_feedstock_mmol(value) | ||
| else | ||
| val_g = self.convert_to_gram(value, unit) |
There was a problem hiding this comment.
Style/RedundantSelf: Redundant self detected.
| calculate_feedstock_mmol(value) | ||
| else | ||
| val_g = self.convert_to_gram(value, unit) | ||
| self.convert_to_unit(val_g, 'mol', true) |
There was a problem hiding this comment.
Style/RedundantSelf: Redundant self detected.
|
@adambasha0 I've opened a new pull request, #2793, to work on those changes. Once the pull request is ready, I'll request review from you. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...cks/src/apps/mydb/elements/details/reactions/schemeTab/GasPhaseReactionDetailsScheme.spec.js
Show resolved
Hide resolved
app/javascript/src/apps/mydb/elements/details/reactions/schemeTab/ReactionDetailsScheme.js
Outdated
Show resolved
Hide resolved
- Guard `calculateGasMoles` against missing/zero temperature to prevent divide-by-zero/NaN - Accept an optional `moles` argument in `calculateVolumeForFeedstockOrGas` and prefer explicit mole input - Update `Sample` model to correctly handle decoupled samples and unify gas-phase condition checks - Normalize purity handling and simplify gram-from-mol conversion in reaction variations helper - Update unit-conversion spec to match new function signature/behavior
…Tab/ReactionDetailsScheme.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // vesselVolume=10L, ppm=1000, T=294K => moles=0.000414 => volume=0.01L | ||
| // For feedstock: volume = (moles * R * 294K) / purity, where moles = amountInGram / molecularWeight | ||
| // purity=0.5 => moles=5 => volume=241.374L | ||
| const gasResult = calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData, 5); |
There was a problem hiding this comment.
The comment at line 79 states "For gas: volume = (moles * R * T), where moles = (ppm * V) / (R * T * 1e6)", but the function signature passes moles=5 as the last parameter. This creates confusion because:
- When
gasType === 'gas', the function recalculatesmolAmountusingcalculateGasMoles(line 145 in UnitsConversion.js), ignoring the passedmolesparameter. - The comment suggests
moles = 0.000414(calculated from ppm), but then passesmoles=5which is never used.
Either update the comment to clarify that the moles parameter is ignored for gas types, or remove the misleading moles=5 from the test call since it has no effect on the gas calculation.
| // vesselVolume=10L, ppm=1000, T=294K => moles=0.000414 => volume=0.01L | |
| // For feedstock: volume = (moles * R * 294K) / purity, where moles = amountInGram / molecularWeight | |
| // purity=0.5 => moles=5 => volume=241.374L | |
| const gasResult = calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData, 5); | |
| // Note: The 'moles' parameter is ignored for gas; it is recalculated from ppm, vessel volume, R, and T. | |
| // vesselVolume=10L, ppm=1000, T=294K => moles=0.000414 => volume=0.01L | |
| // For feedstock: volume = (moles * R * 294K) / purity, where moles = amountInGram / molecularWeight | |
| // purity=0.5 => moles=5 => volume=241.374L | |
| const gasResult = calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData, null); |
| purity_factor = purity || 1.0 | ||
| ideal_gas_constant = 0.0821 | ||
| default_temp_k = 294.0 | ||
| (value * purity_factor * 1000) / (ideal_gas_constant * default_temp_k) |
There was a problem hiding this comment.
The calculate_feedstock_mmol method doesn't handle the case when unit is neither 'g' nor 'l'. This will cause the method to implicitly return nil for unexpected units, which could lead to runtime errors when the return value is used in calculations.
Add an else clause to return 0 or raise an appropriate error for unsupported units:
else
0
end| (value * purity_factor * 1000) / (ideal_gas_constant * default_temp_k) | |
| (value * purity_factor * 1000) / (ideal_gas_constant * default_temp_k) | |
| else | |
| 0 |
| updatedSamplesForVesselSizeChange(samples) { | ||
| const vesselSize = GasPhaseReactionStore.getState().reactionVesselSizeValue; | ||
| return samples.map((sample) => { | ||
| if (sample.gas_type === 'gas' && sample.gas_phase_data) { | ||
| const { part_per_million, temperature } = sample.gas_phase_data; | ||
| let temperatureInKelvin = temperature.value; | ||
| if (temperature.unit !== 'K') { | ||
| temperatureInKelvin = convertTemperatureToKelvin({ value: temperature.value, unit: temperature.unit }); | ||
| } | ||
| const moles = calculateGasMoles(vesselSize, part_per_million, temperatureInKelvin); | ||
| sample.setAmount({ value: moles, unit: 'mol' }); | ||
| sample.updateTONValue(moles); | ||
| } | ||
| return sample; | ||
| }); | ||
| } |
There was a problem hiding this comment.
The updatedSamplesForVesselSizeChange method updates moles and TON for gas products when vessel size changes, but it doesn't recalculate the equivalent (yield) value. According to the PR description, "recalculate dependent gas-product fields when vessel volume changes" should include the equivalent calculation.
The equivalent for gas products is calculated in calculateEquivalentForGasProduct() which depends on vessel volume. When vessel volume changes, the equivalent should be updated similar to how it's done in updatedReactionForGasProductFieldsChange (lines 595-596):
const equivalent = this.calculateEquivalentForGasProduct(sample);
sample.equivalent = equivalent;| }); | ||
|
|
||
| describe('Feedstock Material Calculations', () => { | ||
| it('should calculate moles from volume using Calculation1: Mol = Purity × 1 × V/(0.0821 × 294)', () => { |
There was a problem hiding this comment.
The comment at line 80 contains a typo in the formula. It states "Mol = Purity × 1 × V/(0.0821 × 294)" but the "1" multiplier is redundant and confusing. The formula should be simplified to "Mol = Purity × V/(0.0821 × 294)" to match the actual calculation on line 93.
| it('should calculate moles from volume using Calculation1: Mol = Purity × 1 × V/(0.0821 × 294)', () => { | |
| it('should calculate moles from volume using Calculation1: Mol = Purity × V/(0.0821 × 294)', () => { |
| assert.strictEqual(calculateVolumeForFeedstockOrGas(null, 2, 0.5, 'gas', gasPhaseData), 0); | ||
| // Parameters: vesselVolume, purity, gasType, gasPhaseData, amountInGram, molecularWeight | ||
| // Invalid temperature should cause calculateGasVolume to return 0 | ||
| assert.strictEqual(calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData, null, null), 0); |
There was a problem hiding this comment.
Superfluous argument passed to function calculateVolumeForFeedstockOrGas.
| assert.strictEqual(calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData, null, null), 0); | |
| assert.strictEqual(calculateVolumeForFeedstockOrGas(10, 0.5, 'gas', gasPhaseData), 0); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
app/javascript/src/apps/mydb/elements/details/reactions/schemeTab/ReactionDetailsScheme.js
Show resolved
Hide resolved
app/javascript/src/apps/mydb/elements/details/reactions/schemeTab/ReactionDetailsScheme.js
Show resolved
Hide resolved
| // Ensure molecule_molecular_weight is set as a property on ALL materials, not just a getter | ||
| // This is needed because cloneDeep in getReactionMaterials won't preserve getters | ||
| [reaction.starting_materials, reaction.reactants, reaction.products].flat().forEach((material) => { | ||
| if (material && material.molecule && material.molecule.molecular_weight) { | ||
| Object.defineProperty(material, 'molecule_molecular_weight', { | ||
| value: material.molecule.molecular_weight, | ||
| writable: true, | ||
| enumerable: true, | ||
| configurable: true | ||
| }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
[nitpick] The comment at line 24-25 states "This is needed because cloneDeep in getReactionMaterials won't preserve getters". While this is a valid workaround, consider whether getReactionMaterials should be updated to handle getters properly instead of adding this property definition logic in multiple places (this pattern is duplicated at lines 24-35 and 78-89). This could be a maintenance concern if the same pattern needs to be repeated elsewhere.
| // Ensure molecule_molecular_weight is set as a property on ALL materials, not just a getter | |
| // This is needed because cloneDeep in getReactionMaterials won't preserve getters | |
| [reaction.starting_materials, reaction.reactants, reaction.products].flat().forEach((material) => { | |
| if (material && material.molecule && material.molecule.molecular_weight) { | |
| Object.defineProperty(material, 'molecule_molecular_weight', { | |
| value: material.molecule.molecular_weight, | |
| writable: true, | |
| enumerable: true, | |
| configurable: true | |
| }); | |
| } | |
| }); | |
| // molecule_molecular_weight property is now set in getReactionMaterials |
There was a problem hiding this comment.
the review comment is valid, but might be suitable for another PR
...script/src/apps/mydb/elements/details/reactions/variationsTab/ReactionVariationsMaterials.js
Show resolved
Hide resolved
| if (sample.isGas()) { | ||
| const equivalent = this.calculateEquivalentForGasProduct(sample); | ||
| sample.equivalent = equivalent; | ||
| if ((prevGasType === 'CAT' && updatedSample.gas_type === 'off') || updatedSample.isCatalyst()) { |
There was a problem hiding this comment.
updatedSample.gas_type === 'off' can be moved to Sample.js
port of refactor(gas-phase): synchronize calculations in ReactionDetailsScheme (#2789) * Refactor(gas-phase): synchronize calculations in ReactionDetailsScheme - correct gas product volume equation - update grams when gas product moles change - update TON when catalyst mole value changes - recalculate dependent gas-product fields when vessel volume changes - refactor unit conversion tests for gas-phase scheme - add ReactionDetailsScheme unit tests for gas-phase reactions * refactor spect tests for gas phase ReactionDetailsScheme * fix: calculate correct equivalent value for feedstock material on reaction save * refactor: - solve copilot messages - allow calculation of feedstock mmol value with gram unit on server level * refactor(gas-phase): fix feedstock/gas mole & volume calculations - Guard `calculateGasMoles` against missing/zero temperature to prevent divide-by-zero/NaN - Accept an optional `moles` argument in `calculateVolumeForFeedstockOrGas` and prefer explicit mole input - Update `Sample` model to correctly handle decoupled samples and unify gas-phase condition checks - Normalize purity handling and simplify gram-from-mol conversion in reaction variations helper - Update unit-conversion spec to match new function signature/behavior * refactor: fix equation for calculating feedstock volume from moles amount * fix: reset TON and TOF on unassigning or deleting a catalyst a catalyst from gas phase reaction
Refactor:
rather 1-story 1-commit than sub-atomic commits
commit title is meaningful => git history search
commit description is helpful => helps the reviewer to understand the changes
code is up-to-date with the latest developments of the target branch (rebased to it or whatever) => ⏩-merge for linear history is favoured
added code is linted
tests are passing (at least locally): we still have some random test failure on CI. thinking of asking spec/examples.txt to be commited
in case the changes are visible to the end-user, video or screenshots should be added to the PR => helps with user testing
testing coverage improvement is improved.
CHANGELOG : add a bullet point on top (optional: reference to github issue/PR )
parallele PR for documentation on docusaurus if the feature/fix is tagged for a release