Skip to content

Commit

Permalink
Fix VRI Utilization output
Browse files Browse the repository at this point in the history
  • Loading branch information
smithkm committed Sep 17, 2024
1 parent d0917c0 commit 57148d8
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 226 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,12 @@ public void init(FileSystemFileResolver resolver, Map<String, Object> controlMap

setControlMap(controlMap);
closeVriWriter();
vriWriter = new VdypOutputWriter(controlMap, resolver);
vriWriter = createWriter(resolver, controlMap);
}

protected VdypOutputWriter createWriter(FileSystemFileResolver resolver, Map<String, Object> controlMap)
throws IOException {
return new VdypOutputWriter(controlMap, resolver);
}

protected abstract BaseControlParser getControlFileParser();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,18 @@ void writeSpecies(VdypLayer layer, VdypSpecies spec) throws IOException {

}

protected float fractionForest(VdypPolygon polygon, VdypLayer layer) {
return polygon.getPercentAvailable() / 100f;
}

float safeMultiply(float value, float factor) {
if (value <= 0) {
return value;
}

return value * factor;
}

/**
* Write the utilization records for a layer or species to the utilization file.
*
Expand All @@ -214,14 +226,16 @@ void writeSpecies(VdypLayer layer, VdypSpecies spec) throws IOException {
* @throws IOException
*/
// V7W_AIU Internalized loop over utilization classes
void writeUtilization(VdypLayer layer, VdypUtilizationHolder utils) throws IOException {
void writeUtilization(VdypPolygon polygon, VdypLayer layer, VdypUtilizationHolder utils) throws IOException {
Optional<String> specId = Optional.empty();
Optional<Integer> specIndex = Optional.empty();
if (utils instanceof VdypSpecies spec) {
specId = Optional.of(spec.getGenus());
specIndex = Optional.of(spec.getGenusIndex());
}

float fractionForest = fractionForest(polygon, layer);

for (var uc : UtilizationClass.values()) {
Optional<Float> height = Optional.empty();
if (uc.index < 1) {
Expand Down Expand Up @@ -249,15 +263,18 @@ void writeUtilization(VdypLayer layer, VdypUtilizationHolder utils) throws IOExc

uc.index,

utils.getBaseAreaByUtilization().getCoe(uc.index), //
utils.getTreesPerHectareByUtilization().getCoe(uc.index), //
utils.getBaseAreaByUtilization().getCoe(uc.index) * fractionForest, //
utils.getTreesPerHectareByUtilization().getCoe(uc.index) * fractionForest, //
height.orElse(EMPTY_FLOAT), //

utils.getWholeStemVolumeByUtilization().getCoe(uc.index), //
utils.getCloseUtilizationVolumeByUtilization().getCoe(uc.index), //
utils.getCloseUtilizationVolumeNetOfDecayByUtilization().getCoe(uc.index), //
utils.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().getCoe(uc.index), //
utils.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization().getCoe(uc.index), //
utils.getWholeStemVolumeByUtilization().getCoe(uc.index) * fractionForest, //
utils.getCloseUtilizationVolumeByUtilization().getCoe(uc.index) * fractionForest, //
utils.getCloseUtilizationVolumeNetOfDecayByUtilization().getCoe(uc.index) * fractionForest, //
utils.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().getCoe(uc.index) * fractionForest, //
safeMultiply(
utils.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization().getCoe(uc.index),
fractionForest
), //

quadMeanDiameter.orElse(layer.getLayerType() == LayerType.PRIMARY ? //
EMPTY_FLOAT : 0f
Expand All @@ -277,13 +294,13 @@ public void writePolygonWithSpeciesAndUtilization(VdypPolygon polygon) throws IO

writePolygon(polygon);
for (var layer : polygon.getLayers().values()) {
writeUtilization(layer, layer);
writeUtilization(polygon, layer, layer);
List<VdypSpecies> specs = new ArrayList<>(layer.getSpecies().size());
specs.addAll(layer.getSpecies().values());
specs.sort(Utils.compareUsing(BaseVdypSpecies::getGenus));
for (var species : specs) {
writeSpecies(layer, species);
writeUtilization(layer, species);
writeUtilization(polygon, layer, species);
}
}
writeSpeciesEndRecord(polygon);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ void testZipOutputFileResolver() throws IOException {

ZipOutputFileResolver resolver = new ZipOutputFileResolver();

MatcherAssert.assertThat(resolver.toPath("file").toString(), Matchers.endsWith("vdyp-lib/vdyp-common/file"));
MatcherAssert.assertThat(resolver.toPath("file").toString(), Matchers.endsWith("lib/vdyp-common/file"));

assertThrows(UnsupportedOperationException.class, () -> resolver.resolveForInput("file"));

MatcherAssert.assertThat(resolver.toString("file"), Matchers.endsWith("vdyp-lib/vdyp-common/file"));
MatcherAssert.assertThat(resolver.toString("file"), Matchers.endsWith("lib/vdyp-common/file"));

for (int i = 0; i < 5; i++) {
OutputStream os = resolver.resolveForOutput("file" + i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,15 @@ void testWriteSpecies() throws IOException {
void testWriteUtilizationForLayer() throws IOException {
try (var unit = new VdypOutputWriter(controlMap, fileResolver);) {

var layer = VdypLayer.build(builder -> {
var polygon = VdypPolygon.build(builder -> {
builder.polygonIdentifier("082E004 615 1988");

builder.biogeoclimaticZone(Utils.getBec("IDF", controlMap));
builder.forestInventoryZone("D");

builder.percentAvailable(100f);
});
var layer = VdypLayer.build(polygon, builder -> {
builder.layerType(LayerType.PRIMARY);

builder.addSpecies(specBuilder -> {
Expand Down Expand Up @@ -233,7 +240,7 @@ void testWriteUtilizationForLayer() throws IOException {
// Should be ignored and computed from BA and TPH
layer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f));

unit.writeUtilization(layer, layer);
unit.writeUtilization(polygon, layer, layer);
}
utilStream.assertContent(
VdypMatchers.hasLines(
Expand All @@ -253,8 +260,15 @@ void testWriteUtilizationForLayer() throws IOException {
void testWriteUtilizationZeroBaseArea() throws IOException {
try (var unit = new VdypOutputWriter(controlMap, fileResolver);) {

var layer = VdypLayer.build(builder -> {
var polygon = VdypPolygon.build(builder -> {
builder.polygonIdentifier("082E004 615 1988");

builder.biogeoclimaticZone(Utils.getBec("IDF", controlMap));
builder.forestInventoryZone("D");

builder.percentAvailable(100f);
});
var layer = VdypLayer.build(polygon, builder -> {
builder.layerType(LayerType.PRIMARY);

builder.addSpecies(specBuilder -> {
Expand Down Expand Up @@ -311,7 +325,7 @@ void testWriteUtilizationZeroBaseArea() throws IOException {
// Should be ignored and computed from BA and TPH
species.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f));

unit.writeUtilization(layer, species);
unit.writeUtilization(polygon, layer, species);
}
utilStream.assertContent(
VdypMatchers.hasLines(
Expand All @@ -335,7 +349,7 @@ void testWritePolygonWithChildren() throws IOException {
VdypPolygon polygon = VdypPolygon.build(builder -> {

builder.polygonIdentifier("082E004 615 1988");
builder.percentAvailable(90f);
builder.percentAvailable(100f);
builder.biogeoclimaticZone(Utils.getBec("IDF", controlMap));
builder.forestInventoryZone("D");
builder.mode(PolygonMode.START);
Expand Down Expand Up @@ -427,7 +441,7 @@ void testWritePolygonWithChildren() throws IOException {

unit.writePolygonWithSpeciesAndUtilization(polygon);
}
polyStream.assertContent(is("082E004 615 1988 IDF D 90 28119 1\n"));
polyStream.assertContent(is("082E004 615 1988 IDF D 100 28119 1\n"));
utilStream.assertContent(
VdypMatchers.hasLines(
"082E004 615 1988 P 0 -1 0.02865 9.29 7.8377 0.1077 0.0000 0.0000 0.0000 0.0000 6.3", //
Expand Down
15 changes: 15 additions & 0 deletions lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@
import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon;
import ca.bc.gov.nrs.vdyp.fip.model.FipSite;
import ca.bc.gov.nrs.vdyp.fip.model.FipSpecies;
import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver;
import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException;
import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser;
import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser;
import ca.bc.gov.nrs.vdyp.io.write.VdypOutputWriter;
import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies;
import ca.bc.gov.nrs.vdyp.model.BecDefinition;
import ca.bc.gov.nrs.vdyp.model.Coefficients;
Expand Down Expand Up @@ -91,6 +93,19 @@ public static void main(final String... args) throws IOException {
}
}

@Override
protected VdypOutputWriter createWriter(FileSystemFileResolver resolver, Map<String, Object> controlMap)
throws IOException {
return new VdypOutputWriter(controlMap, resolver) {

@Override
protected float fractionForest(VdypPolygon polygon, VdypLayer layer) {
return 1f;
}

};
}

// FIP_SUB
// TODO Fortran takes a vector of flags (FIPPASS) controlling which stages are
// implemented. FIPSTART always uses the same vector so far now that's not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ protected void check(Collection<String> errors) {

@Override
protected VriLayer doBuild() {
float multiplier = percentAvailable.orElse(100f) / 100f;
float multiplier = 100f / percentAvailable.orElse(100f);
VriLayer result = new VriLayer(
polygonIdentifier.get(), //
layerType.get(), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,10 @@ primaryResult, allOf(
hasProperty("crownClosure", is(95f)), //
hasProperty("utilization", is(9f)), hasProperty(
"baseArea", //
present(closeTo(20f * 0.75f))
present(closeTo(20f * (1f / 0.75f)))
), // Apply Layer Percent Available
hasProperty("treesPerHectare", present(closeTo(300f * 0.75f))) // Apply Layer Percent Available
hasProperty("treesPerHectare", present(closeTo(300f * (1f / 0.75f)))) // Apply Layer Percent
// Available
)
);

Expand Down
Loading

0 comments on commit 57148d8

Please sign in to comment.