Skip to content

Commit

Permalink
Merge pull request #6 from brewerwall/feature/spirit
Browse files Browse the repository at this point in the history
Distillate Unit Type and Spirt Calculations
griffithben authored Sep 18, 2023
2 parents c06a5af + 3d25f08 commit c8ead8f
Showing 8 changed files with 471 additions and 1 deletion.
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -99,10 +99,11 @@ $temperature->getFahrenheit(); // 76
| Gravity | Plato<br/>SpecificGravity<br/>Brix |
| Pressure | Psi<br/>Bar |
| Temperature | Celsius<br/>Fahrenheit |
| Volumne | Ounce<br/>Gallon<br/>Barrel<br/>Milliliter<br/>Liter<br/>Hectoliter |
| Volume | Ounce<br/>Gallon<br/>Barrel<br/>Milliliter<br/>Liter<br/>Hectoliter |
| Weight | Ounce<br/>Pound<br/>Gram<br/>Kilogram |
| Color | Srm<br/>Ebc<br/>Lovibond |
| Time | Millisecond<br/>Second<br/>Minute<br/>Hour<br/>Day<br/>Week<br/>Month<br/>Year |
| Distillate | Proof<br/>Alcohol Percent |

### Preferences

@@ -120,6 +121,7 @@ preference set, but can be overridden when instantiating a new unit.
'Weight' => 'Pound',
'Color' => 'Srm',
'Time' => 'Minute',
'Distillate' => 'Proof',
];
```

@@ -380,3 +382,67 @@ Beer::gravityCorrection(Gravity $gravity, Temperature $temperature, Temperature
##### Returns

- `Gravity` - Corrected Gravity of Sample

### Spirit

This class will calculate Spirit related calculations.
___

#### Dilute Down To Desired Proof

Dilute Down To Desired Proof is a calculation to determine how much water to add to a spirit to get to a desired proof.

```php
Spirit::diluteDownToDesiredProof(Proof $currentProof, Proof $desiredProof, Volume $currentVolume): Volume
```

##### Arguments

- `Proof $currentProof` - Current Proof of the spirit
- `Proof $desiredProof` - Desired Proof of the spirit
- `Volume $currentVolume` - Current Volume of the spirit

##### Returns

- `Volume` - Volume of water to add to the spirit

---

#### Distilled Alcohol Volume

Distilled Alcohol Volume is a calculation to determine the volume of alcohol distilled depending on the wash abv and
still efficiency.

```php
Spirit::distilledAlcoholVolume(Volume $volume, Distillate $wash, float $stillEfficiencyPercent): Volume
```

##### Arguments

- `Volume $volume` - Volume of the wash
- `Distillate $wash` - Distillate of the wash
- `float $stillEfficiencyPercent` - Still efficiency percentage

##### Returns

- `Volume` - Volume of the distilled alcohol

---

#### Distilled Remaining Water Volume

Distilled Remaining Water Volume is a calculation to determine the volume of water remaining after distilling a spirit.

```php
Spirit::distilledRemainingWaterVolume(Volume $volume, Distillate $wash, float $stillEfficiencyPercent): Volume
```

##### Arguments

- `Volume $volume` - Volume of the wash
- `Distillate $wash` - Distillate of the wash
- `float $stillEfficiencyPercent` - Still efficiency percentage

##### Returns

- `Volume` - Volume of the remaining water
1 change: 1 addition & 0 deletions src/BaseUnitz.php
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ class BaseUnitz
'Weight' => 'Pound',
'Color' => 'Srm',
'Time' => 'Minute',
'Distillate' => 'Proof'
];

private array $preferences;
79 changes: 79 additions & 0 deletions src/Calculate/Spirit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Unitz\Calculate;

use InvalidArgumentException;
use Unitz\Distillate;
use Unitz\Volume;

class Spirit
{
/**
* The amount of water you need to add to a distillate to dilute it down to a desired distillate.
*
* @param \Unitz\Distillate $current
* @param \Unitz\Distillate $desired
* @param \Unitz\Volume $distillateVolume
* @return \Unitz\Volume
* @throws \RuntimeException
*/
public static function diluteDownToDesiredProof(
Distillate $current,
Distillate $desired,
Volume $distillateVolume
): Volume {
if ($current->getPercentAlcohol() < $desired->getPercentAlcohol()) {
throw new InvalidArgumentException('Current distillate cannot be less than desired distillate.');
}

return new Volume(
liter: $distillateVolume->getLiter() * (($current->getPercentAlcohol() / $desired->getPercentAlcohol()) - 1)
);
}

/**
* Determines the Volume of distillate you will get with a specific wash abv and still efficiency.
*
* Source - https://www.hillbillystills.com/distilling-calculator
*
* @param \Unitz\Volume $volume
* @param \Unitz\Distillate $wash
* @param float $stillEfficiencyPercent
* @return \Unitz\Volume
* @throws \InvalidArgumentException
*/
public static function distilledAlcoholVolume(
Volume $volume,
Distillate $wash,
float $stillEfficiencyPercent
): Volume {
if ($stillEfficiencyPercent === 0.0) {
throw new InvalidArgumentException('Still Efficiency cannot be zero.');
}

return new Volume(
liter: ((0.95 * $volume->getLiter() * $wash->getPercentAlcohol() / $stillEfficiencyPercent) * 100) / 100
);
}

/**
* @param \Unitz\Volume $volume
* @param \Unitz\Distillate $wash
* @param float $stillEfficiencyPercent
* @return \Unitz\Volume
* @throws \InvalidArgumentException
*/
public static function distilledRemainingWaterVolume(
Volume $volume,
Distillate $wash,
float $stillEfficiencyPercent
): Volume {
return new Volume(
liter: $volume->getLiter() - self::distilledAlcoholVolume(
$volume,
$wash,
$stillEfficiencyPercent
)->getLiter()
);
}
}
106 changes: 106 additions & 0 deletions src/Distillate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Unitz;

use InvalidArgumentException;

class Distillate extends AbstractUnitz
{
private float $proof;
private float $percentAlcohol;

public function __construct(
?float $proof = null,
?float $percentAlcohol = null,
?float $userValue = null,
array $preferences = []
) {
if (!$this->hasOnlyOneValue([$proof, $percentAlcohol, $userValue])) {
throw new InvalidArgumentException('Only one Distillate type can be set at a time.');
}

parent::__construct($preferences);

if (is_numeric($proof)) {
$this->setProof($proof);
}

if (is_numeric($percentAlcohol)) {
$this->setPercentAlcohol($percentAlcohol);
}

if (is_numeric($userValue)) {
$this->setValue($userValue);
}
}

/**
* @param float $proof
* @return $this
* @throws \InvalidArgumentException
*/
public function setProof(float $proof): self
{
if ($proof > 200) {
throw new InvalidArgumentException('Proof cannot be greater than 200');
}

$this->proof = $proof;
$this->percentAlcohol = self::convertProofToPercentAlcohol($proof);

return $this;
}

/**
* @param ?int $round
* @return float
*/
public function getProof(?int $round = null): float
{
return $round ? round($this->proof, $round) : $this->proof;
}

/**
* @param float $percentAlcohol
* @return $this
* @throws \InvalidArgumentException
*/
public function setPercentAlcohol(float $percentAlcohol): self
{
if ($percentAlcohol > 100) {
throw new InvalidArgumentException('Percent alcohol cannot be greater than 100');
}

$this->percentAlcohol = $percentAlcohol;
$this->proof = self::convertPercentAlcoholToProof($percentAlcohol);

return $this;
}

/**
* @param ?int $round
* @return float
*/
public function getPercentAlcohol(?int $round = null): float
{
return $round ? round($this->percentAlcohol, $round) : $this->percentAlcohol;
}

/**
* @param float $proof
* @return float
*/
public static function convertProofToPercentAlcohol(float $proof): float
{
return $proof / 2;
}

/**
* @param float $percentAlcohol
* @return float
*/
public static function convertPercentAlcoholToProof(float $percentAlcohol): float
{
return $percentAlcohol * 2;
}
}
14 changes: 14 additions & 0 deletions src/UnitzService.php
Original file line number Diff line number Diff line change
@@ -146,4 +146,18 @@ public function makeTime(
$this->getPreferences()
);
}

/**
* @param float|null $proof
* @param float|null $percentAlcohol
* @param float|null $userValue
* @return \Unitz\Distillate
*/
public function makeDistillate(
?float $proof = null,
?float $percentAlcohol = null,
?float $userValue = null
): Distillate {
return new Distillate($proof, $percentAlcohol, $userValue, $this->getPreferences());
}
}
81 changes: 81 additions & 0 deletions tests/Calculate/SpiritTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Tests\Calculate;

use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Unitz\Calculate\Spirit;
use Unitz\Distillate;
use Unitz\Volume;

class SpiritTest extends TestCase
{
public function testDiluteDownToDesiredProofCalculatesCorrectly(): void
{
$distillateVolume = new Volume(liter: 2);
$current = new Distillate(percentAlcohol: 75);
$desired = new Distillate(percentAlcohol: 40);
$expected = new Volume(liter: 1.75);

$actual = Spirit::diluteDownToDesiredProof($current, $desired, $distillateVolume);
$this->assertEquals($expected, $actual);
}

public function testDiluteDownToDesiredProofThrowsInvalidArgumentExceptionWithReverseDistillateValues(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Current distillate cannot be less than desired distillate.');

$distillateVolume = new Volume(liter: 2);
$current = new Distillate(percentAlcohol: 35);
$desired = new Distillate(percentAlcohol: 40);

Spirit::diluteDownToDesiredProof($current, $desired, $distillateVolume);
}

public function testDistilledAlcoholVolumeCalculatesCorrectly(): void
{
$volume = new Volume(liter: 20);
$wash = new Distillate(percentAlcohol: 9);
$stillEfficiencyPercent = 92;
$expected = new Volume(liter: 1.858695652173913);

$actual = Spirit::distilledAlcoholVolume($volume, $wash, $stillEfficiencyPercent);
$this->assertEquals($expected, $actual);
}

public function testDistilledAlcoholVolumeThrowsInvalidArgumentExceptionWithZeroStillEfficiency(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Still Efficiency cannot be zero.');

$volume = new Volume(liter: 20);
$wash = new Distillate(percentAlcohol: 9);
$stillEfficiencyPercent = 0;

Spirit::distilledAlcoholVolume($volume, $wash, $stillEfficiencyPercent);
}

public function testDistilledRemainingWaterVolumeCalculatesCorrectly(): void
{
$volume = new Volume(liter: 20);
$wash = new Distillate(percentAlcohol: 9);
$stillEfficiencyPercent = 92;
$expected = new Volume(liter: 18.141304347826086);

$actual = Spirit::distilledRemainingWaterVolume($volume, $wash, $stillEfficiencyPercent);
$this->assertEquals($expected, $actual);
}

public function testDistilledRemainingWaterVolumeThrowsInvalidArgumentExceptionWithZeroStillEfficiency(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Still Efficiency cannot be zero.');

$volume = new Volume(liter: 20);
$wash = new Distillate(percentAlcohol: 9);
$stillEfficiencyPercent = 0;

Spirit::distilledRemainingWaterVolume($volume, $wash, $stillEfficiencyPercent);
}
}
Loading

0 comments on commit c8ead8f

Please sign in to comment.