Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "livestock-rs"
version = "0.10.0"
version = "0.12.0"
edition = "2021"
authors = ["Jaken Herman <jaken@rowanranch.com>"]
license = "MIT"
Expand Down
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A comprehensive library and CLI tool for managing, identifying, and working with

Features
- 🌱 Support for over 1,000+ livestock breeds, categorized by species.
- 📊 Provides calculators for growth metrics like Average Daily Gain (ADG).
- 📊 Provides calculators for growth metrics like Average Daily Gain (ADG) and Feed Conversion Ration (FCR).
- 🐄 Includes common cattle breeds like Angus and Brahman.
- 🐐 Includes common goat breeds like Alpine and Boer.
- 🐑 Includes common sheep breeds like Dorper and St. Croix.
Expand All @@ -24,7 +24,7 @@ Add the crate to your `Cargo.toml`:

```
[dependencies]
livestock_rs = "0.10.0"
livestock_rs = "0.12.0"
```

or
Expand All @@ -39,6 +39,31 @@ use livestock_rs::calculators::growth::adg::calculate_adg;
let adg = calculate_adg(100.0, 150.0, 50);
```

For CLI, use
```
stocktools adg -i 100 -f 150 -d 50
```

## FCR & Feed Efficiency Rating Usage Example
``` rust
use livestock_rs::calculators::feed::fcr::calculate_fcr;
use livestock_rs::calculators::feed::efficiency::{calculate_feed_efficiency, FeedEfficiency, FeedEfficiencyRating};
use livestock_rs::types::LivestockType;

let fcr = calculate_fcr(100.0, 30.0); // FCR = 3.33
let feed_efficiency = calculate_feed_efficiency(fcr, LivestockType::Swine)?; // Swine should be from 3.0 - 3.9

// feed_efficiency.rating = FeedEfficiencyRating::Average
// feed_efficiency.fcr = 3.33
// feed_efficiency.min_fcr = 3.0
// feed_efficiency.max_fcr = 3.9
```

For CLI, use
```
stocktools fcr -i 100 -g 300
```

## Breed Usage Example
``` rust
use livestock_rs::breeds::GoatBreed;
Expand Down
2 changes: 1 addition & 1 deletion src/bin/stocktools/adg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use livestock_rs::calculators::growth::adg::calculate_adg;
Calculate the ADG for an animal that starts at 100 kg and ends at 150 kg over 50 days:

```
livestock adg -i 100 -f 150 -d 50
stocktools adg -i 100 -f 150 -d 50
```

The result will be `1.0`, which means the animal gained 1 kg per day.
Expand Down
81 changes: 81 additions & 0 deletions src/bin/stocktools/efficiency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use anyhow::{anyhow, Context, Result};
use clap::Parser;
use livestock_rs::{calculators::feed::{efficiency::calculate_feed_efficiency, fcr::calculate_fcr}, types::LivestockType};

#[derive(Parser, Debug)]
#[command(
arg_required_else_help(true),
about = "Calculate Feed Efficiency for livestock.",
long_about = "
Calculate Feed Efficiency for livestock.

The Feed Efficiency is a measure of how well livestock convert feed into body mass. It is
calculated using the Feed Conversion Ratio (FCR) of the animal.

The formula is:

Feed Efficiency = 1 / FCR

where:

- `Feed Efficiency` is the efficiency of feed conversion.
- `FCR` is the Feed Conversion Ratio of the animal.

# Example

Calculate the Feed Efficiency for an animal with a FCR of 2.0:

```
stocktools feed-efficiency --fcr 2.0 --livestock-type Cattle
```

The result will be `0.5`, which means the animal converted feed with an efficiency of 0.5. This is very good for cattle.
"
)]
pub struct FeedEfficiencySubcommand {
#[arg(help = "Amount of feed intake (in kg or lbs)", long, short = 'i')]
feed_intake: Option<f64>,
#[arg(help = "Weight gain of livestock (in kg or lbs)", long, short = 'g')]
weight_gain: Option<f64>,
#[arg(help = "Feed efficiency ratio (FCR)", long)]
fcr: Option<f64>,
#[arg(
long,
help = "The type of livestock.",
short = 't',
)]
livestock_type: LivestockType,
}

impl FeedEfficiencySubcommand {
pub fn run(&self) -> Result<()> {
// ensure that we either have feed intake and weight gain or FCR
let fcr = match (self.feed_intake, self.weight_gain, self.fcr) {
(Some(feed_intake), Some(weight_gain), None) => {
calculate_fcr(feed_intake, weight_gain).context(
"Failed to calculate FCR, which is required if feed intake and weight gain are provided.
FCR is needed to calculate feed efficiency.",
)?
}
(None, None, Some(fcr)) => fcr,
_ => {
return Err(anyhow!(
"Either feed intake and weight gain or FCR must be provided."
))
}
};

let feed_efficiency = calculate_feed_efficiency(fcr, self.livestock_type.clone()).with_context(|| format!(
"Failed to calculate feed efficiency with FCR: {} and livestock type: {:?}.",
fcr, self.livestock_type
))?;

println!(" ");
println!("Feed Efficiency Rating: {:?}", feed_efficiency.rating);
println!("FCR: {:.2}", feed_efficiency.value);
println!("{:?} should aim for a FCR between {:.2} and {:.2}.", self.livestock_type, feed_efficiency.avg_min_fcr, feed_efficiency.avg_max_fcr);
println!(" ");

Ok(())
}
}
52 changes: 52 additions & 0 deletions src/bin/stocktools/fcr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use anyhow::{Context, Result};
use clap::Parser;
use livestock_rs::calculators::feed::fcr::calculate_fcr;

#[derive(Parser, Debug)]
#[command(
arg_required_else_help(true),
about = "Calculate Feed Conversion Ratio (FCR) for livestock.",
long_about = "
Calculate Feed Conversion Ratio (FCR) for livestock.

The Feed Conversion Ratio (FCR) is a measure of feed efficiency that indicates the amount of
feed required to produce a unit of weight gain in livestock. It is calculated by dividing the
amount of feed consumed by the weight gain of the animal.

The formula is:

FCR = feed_intake / weight_gain

where:

- `FCR` is the feed conversion ratio as a ratio of feed intake to weight gain.
- `feed_intake` is the amount of feed consumed by the animal (in kg or lbs).
- `weight_gain` is the weight gain of the animal (in kg or lbs).

# Example

Calculate the FCR for an animal that consumes 100 kg of feed and gains 150 kg:

```
livestock fcr -i 100 -g 150
```

The result will be `0.67`, which means the animal required 0.67 kg of feed per kg of weight gain.
"
)]
pub struct FcrSubcommand {
#[arg(help = "Amount of feed intake (in kg or lbs)", long, short = 'i')]
feed_intake: f64,
#[arg(help = "Weight gain of livestock (in kg or lbs)", long, short = 'g')]
weight_gain: f64,
}

impl FcrSubcommand {
pub fn run(&self) -> Result<()> {
let fcr = calculate_fcr(self.feed_intake, self.weight_gain)
.context("Failed to calculate FCR.")?;

println!("Feed Conversion Ratio (FCR): {:.2}", fcr);
Ok(())
}
}
10 changes: 10 additions & 0 deletions src/bin/stocktools/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ use clap::{Parser, Subcommand};
mod adg;
use adg::AdgSubcommand;

mod efficiency;
use efficiency::FeedEfficiencySubcommand;

mod fcr;
use fcr::FcrSubcommand;

#[derive(Subcommand, Debug)]
enum Commands {
Adg(AdgSubcommand),
Fcr(FcrSubcommand),
FeedEfficiency(FeedEfficiencySubcommand),
}

#[derive(Parser)]
Expand All @@ -28,5 +36,7 @@ fn main() -> Result<()> {

match cli.command {
Commands::Adg(subcommand) => subcommand.run(),
Commands::Fcr(subcommand) => subcommand.run(),
Commands::FeedEfficiency(subcommand) => subcommand.run(),
}
}
118 changes: 118 additions & 0 deletions src/calculators/feed/efficiency.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use anyhow::{ensure, Result};
use crate::types::LivestockType;

#[derive(Debug, Eq, PartialEq)]
pub enum FeedEfficiencyRating {
Poor,
Average,
Good,
}

#[derive(Debug, PartialEq)]
pub struct FeedEfficiency {
pub rating: FeedEfficiencyRating,
pub value: f64,
pub avg_min_fcr: f64,
pub avg_max_fcr: f64
}

/// Calculate Feed Efficiency for livestock.
///
/// # Arguments
/// - `fcr`: Feed Conversion Ratio (FCR) of the animal.
/// - `livestock_type`: The type of livestock.
///
/// # Returns
/// The feed efficiency of the animal.
///
/// # Example
/// ```
/// use livestock_rs::calculators::feed::efficiency::{calculate_feed_efficiency, FeedEfficiency, FeedEfficiencyRating};
/// use livestock_rs::types::LivestockType;
///
/// let fcr = 10.0;
/// let livestock_type = LivestockType::Cattle;
///
/// let feed_efficiency = calculate_feed_efficiency(fcr, livestock_type).unwrap();
///
/// // The feed efficiency is average for a FCR of 10.0 for cattle.
/// assert_eq!(feed_efficiency.rating, FeedEfficiencyRating::Average);
///
/// ```
///
pub fn calculate_feed_efficiency(fcr: f64, livestock_type: LivestockType) -> Result<FeedEfficiency> {
ensure!(fcr > 0.0, "Feed Conversion Ratio (FCR) must be greater than 0.");

// Feed efficiency values for different animals.
//
// Source: <https://www.farmbrite.com/post/feed-conversion-ratio-calculator>
let (min_fcr, max_fcr) = match livestock_type {
LivestockType::Cattle => (8.0, 12.0),
LivestockType::Goat | LivestockType::Sheep => (4.5, 5.5),
LivestockType::Swine => (3.0, 3.9),
LivestockType::Chicken => (1.5, 2.0),
LivestockType::Rabbit => (3.5, 5.0),
};

let feed_efficiency = if fcr > max_fcr {
FeedEfficiency {
rating: FeedEfficiencyRating::Poor,
value: fcr,
avg_min_fcr: min_fcr,
avg_max_fcr: max_fcr
}
} else if fcr < min_fcr {
FeedEfficiency {
rating: FeedEfficiencyRating::Good,
value: fcr,
avg_min_fcr: min_fcr,
avg_max_fcr: max_fcr
}
} else {
FeedEfficiency {
rating: FeedEfficiencyRating::Average,
value: fcr,
avg_min_fcr: min_fcr,
avg_max_fcr: max_fcr
}
};

Ok(feed_efficiency)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::types::LivestockType;

#[test]
fn test_calculate_feed_efficiency() {
let feed_efficiency_test_cases = [
(10.0, LivestockType::Cattle, FeedEfficiencyRating::Average),
(4.0, LivestockType::Cattle, FeedEfficiencyRating::Good),
(2.0, LivestockType::Cattle, FeedEfficiencyRating::Good),
(15.0, LivestockType::Cattle, FeedEfficiencyRating::Poor),
(5.0, LivestockType::Goat, FeedEfficiencyRating::Average),
(4.5, LivestockType::Goat, FeedEfficiencyRating::Average),
(5.5, LivestockType::Goat, FeedEfficiencyRating::Average),
(3.0, LivestockType::Swine, FeedEfficiencyRating::Average),
(3.9, LivestockType::Swine, FeedEfficiencyRating::Average),
(1.5, LivestockType::Chicken, FeedEfficiencyRating::Average),
(2.0, LivestockType::Chicken, FeedEfficiencyRating::Average),
(3.5, LivestockType::Rabbit, FeedEfficiencyRating::Average),
(5.0, LivestockType::Rabbit, FeedEfficiencyRating::Average),
];

for (fcr, livestock_type, expected) in feed_efficiency_test_cases.iter() {
let result = calculate_feed_efficiency(*fcr, livestock_type.clone());
assert!(result.is_ok());
assert_eq!(result.unwrap().rating, *expected);
}
}

#[test]
fn test_calculate_feed_efficiency_zero_fcr() {
let result = calculate_feed_efficiency(0.0, LivestockType::Cattle);
assert!(result.is_err());
}
}
Loading
Loading