Skip to content

Commit

Permalink
feat: differences in gw and pollen (#1585)
Browse files Browse the repository at this point in the history
  • Loading branch information
RMCampos authored Sep 24, 2024
1 parent 62ff7b9 commit 545c686
Show file tree
Hide file tree
Showing 22 changed files with 591 additions and 397 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,18 @@ public record OrchardParentTreeValsDto(
BigDecimal pollenCount,
@Schema(
description =
"A float number representing the value for the SMP success on parent percentage",
"A number representing the value for the SMP success on parent percentage",
example = "5",
type = "number",
format = "int")
Integer smpSuccessPerc,
@Schema(
description =
"A number representing the non-orchard pollen contamination (%) of the parent tree.",
example = "15",
type = "number",
format = "int")
Integer nonOrchardPollenContamPct,
@Schema(
description =
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ca.bc.gov.backendstartapi.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
import java.util.List;

/** This class represents the JSON that will be returned by the GW calculations. */
Expand All @@ -16,4 +17,7 @@ public record PtCalculationResDto(
@Schema(description = "Various calculated value for the orchard parent trees.")
CalculatedParentTreeValsDto calculatedPtVals,
@Schema(description = "The calculated mean geospatial values for SMP mix.")
GeospatialRespondDto smpMixMeanGeoData) {}
GeospatialRespondDto smpMixMeanGeoData,
@Schema(description = "The calculated SMP Success percentage") BigDecimal smpSuccessPct,
@Schema(description = "The calculated Orchard Contamination percentage")
BigDecimal orchardContaminationPct) {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;

/** This class represents the JSON request body when doing parent tree and SMP calculations. */
Expand All @@ -11,4 +12,5 @@
public record PtValsCalReqDto(
@NotNull List<OrchardParentTreeValsDto> orchardPtVals,
@NotNull List<GeospatialRequestDto> smpMixIdAndProps,
@NotNull Integer smpParentsOutside) {}
@NotNull Integer smpParentsOutside,
@NotNull BigDecimal contaminantPollenBv) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ca.bc.gov.backendstartapi.dto;

import io.swagger.v3.oas.annotations.media.Schema;

/** This record represents the seedlot SMP Parents from outside number. */
@Schema(description = "The seedlot SMP Parents from outside number.")
public record SeedlotFormSmpParentOutsideDto(Integer smpParentsOutside) {}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public record SeedlotFormSubmissionDto(
SeedlotFormOrchardDto seedlotFormOrchardDto,
List<SeedlotFormParentTreeSmpDto> seedlotFormParentTreeDtoList,
List<SeedlotFormParentTreeSmpDto> seedlotFormParentTreeSmpDtoList,
SeedlotFormSmpParentOutsideDto seedlotFormSmpParentOutsideDto,
SeedlotFormExtractionDto seedlotFormExtractionDto,
List<SeedlotReviewSeedPlanZoneDto> seedlotReviewSeedPlanZones,
SeedlotReviewElevationLatLongDto seedlotReviewElevationLatLong,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@
import ca.bc.gov.backendstartapi.config.SparLog;
import ca.bc.gov.backendstartapi.dto.CodeDescriptionDto;
import ca.bc.gov.backendstartapi.dto.GeneticWorthDto;
import ca.bc.gov.backendstartapi.dto.GeneticWorthTraitsDto;
import ca.bc.gov.backendstartapi.dto.OrchardParentTreeValsDto;
import ca.bc.gov.backendstartapi.dto.PtCalculationResDto;
import ca.bc.gov.backendstartapi.entity.GeneticWorthEntity;
import ca.bc.gov.backendstartapi.exception.NoGeneticWorthException;
import ca.bc.gov.backendstartapi.repository.GeneticWorthRepository;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -59,44 +55,6 @@ public CodeDescriptionDto getGeneticWorthByCode(@NonNull String code) {
.orElseThrow(NoGeneticWorthException::new);
}

/**
* Does the calculation for each genetic trait. PS: if the threshold of 70% of contribution from
* the parent tree is not met, the trait value will not be shown.
*
* @param traitsDto A {@link List} of {@link OrchardParentTreeValsDto} with the traits and values
* to be calculated.
* @return A {@link PtCalculationResDto} containing all calculated values
*/
public List<GeneticWorthTraitsDto> calculateGeneticWorth(
List<OrchardParentTreeValsDto> traitsDto) {
SparLog.info("Starting Genetic Worth calculations");
BigDecimal minimumThreshold = new BigDecimal("0.7");

List<GeneticWorthTraitsDto> calculated = new ArrayList<>();

// Iterate over all traits
List<GeneticWorthDto> geneticWorthList = getAllGeneticWorth();

for (GeneticWorthDto trait : geneticWorthList) {
BigDecimal calculatedValue = null;
BigDecimal percentage = calcGeneticTraitThreshold(traitsDto, trait);

if (percentage.compareTo(minimumThreshold) >= 0) {
SparLog.info("Calculating Genetic Worth for {} trait", trait.getCode());
calculatedValue = calculateTraitGeneticWorth(traitsDto, trait);
} else {
SparLog.info(
"No Genetic Worth calculations for trait {}, threshold not met.", trait.getCode());
}

GeneticWorthTraitsDto traitResponse =
new GeneticWorthTraitsDto(trait.getCode(), null, calculatedValue, percentage);
calculated.add(traitResponse);
}

return calculated;
}

/**
* Does the Ne calculation (effective population size).
*
Expand Down Expand Up @@ -156,127 +114,4 @@ public BigDecimal calculateNe(

return varEffectivePopSize.setScale(1, halfUp);
}

/**
* Do the calculations for each Genetic Worth trait. Note that in the case the parent tree does
* not attend the 70% threshold weight, the value for this trait will be zero.
*
* @param traitsDto A {@link List} of {@link OrchardParentTreeValsDto} with the traits and values.
* @return A {@link BigDecimal} representing the value.
*/
private BigDecimal calculateTraitGeneticWorth(
List<OrchardParentTreeValsDto> traitsDto, CodeDescriptionDto trait) {
BigDecimal malePollenSum = reducePollenCount(traitsDto);
BigDecimal femaleConeSum = reduceConeCount(traitsDto);
BigDecimal sumGw = BigDecimal.ZERO;

for (OrchardParentTreeValsDto dto : traitsDto) {
BigDecimal traitBreedingValue = getTraitValue(dto, trait);
if (!Objects.isNull(traitBreedingValue) && traitBreedingValue.floatValue() != 0) {
BigDecimal pi =
calculatePi(dto.pollenCount(), dto.coneCount(), malePollenSum, femaleConeSum);
BigDecimal gwValue = traitBreedingValue.multiply(pi);
sumGw = sumGw.add(gwValue);
}
}

return sumGw;
}

/**
* Calculate the threshold of a genetic trait. To be used to check if a given trait met the
* minimum of 70% of parent tree contribution.
*
* @param traitsDto A {@link List} of {@link OrchardParentTreeValsDto} with the traits and values
* to be calculated.
* @param trait A {@link GeneticWorth} with the trait that should be considered.
* @return A BigDecimal representing the trait threshold.
*/
private BigDecimal calcGeneticTraitThreshold(
List<OrchardParentTreeValsDto> traitsDto, CodeDescriptionDto trait) {
SparLog.debug("Checking genetic trait threshold for {} trait", trait);

BigDecimal malePollenSum = reducePollenCount(traitsDto);
BigDecimal femaleConeSum = reduceConeCount(traitsDto);
BigDecimal sumPi = BigDecimal.ZERO;

SparLog.debug("femaleConeSum {}", femaleConeSum);
SparLog.debug("malePollenSum {}", malePollenSum);

for (OrchardParentTreeValsDto traitFor : traitsDto) {
BigDecimal traitBreedingValue = getTraitValue(traitFor, trait);
if (!Objects.isNull(traitBreedingValue) && traitBreedingValue.floatValue() != 0) {
BigDecimal pi =
calculatePi(traitFor.pollenCount(), traitFor.coneCount(), malePollenSum, femaleConeSum);
sumPi = sumPi.add(pi);
}
}

SparLog.debug("Finished checking Genetic Trait threshold for {} trait with: {}%", trait, sumPi);

return sumPi;
}

/**
* Finds the genetic trait value given the request dto and the trait that should be found.
*
* @param dto A {@link OrchardParentTreeValsDto} instance with the traits value.
* @param trait A {@link GeneticWorth} with the trait that should be considered.
* @return a BigDecimal representing the trait value or BigDecimal.ZERO otherwise.
*/
private BigDecimal getTraitValue(OrchardParentTreeValsDto traitDto, CodeDescriptionDto trait) {
List<GeneticWorthTraitsDto> geneticTraits = traitDto.geneticTraits();
Optional<GeneticWorthTraitsDto> traitOptional =
geneticTraits.stream()
.filter(x -> x.traitCode().equalsIgnoreCase(trait.getCode()))
.findFirst();
return traitOptional.isEmpty() ? BigDecimal.ZERO : traitOptional.get().traitValue();
}

/**
* Calculate the pi. The P represents the percentage of contribution for a parent tree, the sum of
* male and female contribution divided by two.
*
* @param pollenCount The amount of pollen for this parent tree (clone).
* @param coneCount The amount of cone for this parent tree (clone).
* @param pollenSum The sum of all pollen count for this calculation.
* @param coneSum The sum of all cone count for this calculation.
* @return A BigDecimal representing the value.
*/
private BigDecimal calculatePi(
BigDecimal pollenCount, BigDecimal coneCount, BigDecimal pollenSum, BigDecimal coneSum) {
BigDecimal bigZero = BigDecimal.ZERO;
BigDecimal mi =
pollenSum.compareTo(bigZero) == 0
? bigZero
: pollenCount.divide(pollenSum, 10, RoundingMode.HALF_UP);
BigDecimal fi =
coneSum.compareTo(bigZero) == 0
? bigZero
: coneCount.divide(coneSum, 10, RoundingMode.HALF_UP);

BigDecimal calculatedPi = mi.add(fi).divide(new BigDecimal("2"), 10, RoundingMode.HALF_UP);

return calculatedPi;
}

/**
* Sums all the pollen count.
*
* @param traitsDto A {@link List} of {@link OrchardParentTreeValsDto} with the traits and values
* @return A BigDecimal representing the value
*/
private BigDecimal reducePollenCount(List<OrchardParentTreeValsDto> traitsDto) {
return traitsDto.stream().map(x -> x.pollenCount()).reduce(BigDecimal.ZERO, BigDecimal::add);
}

/**
* Sums all the cone count.
*
* @param traitsDto A {@link List} of {@link OrchardParentTreeValsDto} with the traits and values
* @return A BigDecimal representing the value
*/
private BigDecimal reduceConeCount(List<OrchardParentTreeValsDto> traitsDto) {
return traitsDto.stream().map(x -> x.coneCount()).reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
Loading

0 comments on commit 545c686

Please sign in to comment.