From 8050fd1659ccbe9488bb098add563340b18f16fe Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 26 Sep 2024 13:38:07 -0700 Subject: [PATCH 01/45] Add Vdyp Back, create VdypBackApplication stub and VdypProcessingApplication to share code with VDYP Forward --- lib/pom.xml | 1 + lib/vdyp-back/pom.xml | 109 ++++++++ .../nrs/vdyp/back/VdypBackApplication.java | 40 +++ .../src/main/resources/application.properties | 24 ++ .../src/main/resources/logging.properties | 3 + .../ca/bc/gov/nrs/vdyp/application/Pass.java | 20 ++ .../gov/nrs/vdyp/application/Processor.java | 103 ++++++++ .../VdypProcessingApplication.java | 122 +++++++++ .../VdypProcessingApplicationTest.java | 236 ++++++++++++++++++ .../bc/gov/nrs/vdyp/vri/model/VriLayer.java | 1 + 10 files changed, 659 insertions(+) create mode 100644 lib/vdyp-back/pom.xml create mode 100644 lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java create mode 100644 lib/vdyp-back/src/main/resources/application.properties create mode 100644 lib/vdyp-back/src/main/resources/logging.properties create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java diff --git a/lib/pom.xml b/lib/pom.xml index 63566ca19..f1de649ab 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -34,6 +34,7 @@ vdyp-sindex vdyp-si32 vdyp-forward + vdyp-back diff --git a/lib/vdyp-back/pom.xml b/lib/vdyp-back/pom.xml new file mode 100644 index 000000000..73bd8d8be --- /dev/null +++ b/lib/vdyp-back/pom.xml @@ -0,0 +1,109 @@ + + 4.0.0 + + vdyp-back + jar + + Variable Density Yield Project - Back + http://maven.apache.org + + + ca.bc.gov.nrs.vdyp + vdyp-lib + 0.0.1-SNAPSHOT + + + + + ca.bc.gov.nrs.vdyp + vdyp-common + 0.0.1-SNAPSHOT + + + ca.bc.gov.nrs.vdyp + vdyp-forward + 0.0.1-SNAPSHOT + + + + ca.bc.gov.nrs.vdyp + vdyp-common + ${project.version} + test-jar + test + + + + org.apache.commons + commons-lang3 + + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-jdk14 + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.hamcrest + hamcrest + test + + + + org.easymock + easymock + test + + + + ca.bc.gov.nrs.vdyp + vdyp-si32 + 0.0.1-SNAPSHOT + + + + + + src/main/resources/application.properties + + + + src/main/resources + true + + + + + src/test/resources + true + + + + + + net.revelc.code.formatter + formatter-maven-plugin + + + ca.bc.gov.nrs.vdyp + vdyp-buildtools + ${project.version} + + + + + + + + diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java new file mode 100644 index 000000000..db74d8d91 --- /dev/null +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java @@ -0,0 +1,40 @@ +package ca.bc.gov.nrs.vdyp.back; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.bc.gov.nrs.vdyp.application.Processor; +import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; +import ca.bc.gov.nrs.vdyp.application.VdypProcessingApplication; + +public class VdypBackApplication extends VdypProcessingApplication { + + static { + initLogging(VdypBackApplication.class); + } + + static final Logger logger = LoggerFactory.getLogger(VdypBackApplication.class); + + public static final String DEFAULT_VDYP_CONTROL_FILE_NAME = "vdyp.ctr"; + + public static void main(final String... args) { + + var app = new VdypBackApplication(); + System.exit(app.run(args)); + } + + @Override + protected Processor getProcessor() { + return null; // TODO + } + + @Override + public String getDefaultControlFileName() { + return DEFAULT_VDYP_CONTROL_FILE_NAME; + } + + @Override + public VdypApplicationIdentifier getId() { + return VdypApplicationIdentifier.VDYP_BACK; + } +} diff --git a/lib/vdyp-back/src/main/resources/application.properties b/lib/vdyp-back/src/main/resources/application.properties new file mode 100644 index 000000000..3fc076a15 --- /dev/null +++ b/lib/vdyp-back/src/main/resources/application.properties @@ -0,0 +1,24 @@ +COMPANY_NAME=Vivid Solutions, Inc. +BINARY_PRODUCT=VDYP7 +BINARY_EXTENSION=JAR +VERSION_MAJOR=8 +VERSION_MINOR=0 +VERSION_INC=0 +VERSION_BUILD=${env.BUILD_NUMBER} +VERSION_YEAR=2023 +VERSION_MONTH=12 +VERSION_DAY=11 +COPYRIGHT_START=2023 +COPYRIGHT_END=2023 +VERSION_CONTROL_SYSTEM=git +VERSION_CONTROL_VERSION=2023 +BRANCH_NAME=main +LAST_COMMIT_REFERENCE= +LAST_COMMIT_AUTHOR= +LAST_COMMIT_DATE= +BUILD_MACHINE=${env.NODE_NAME} +ENV_COMPILER=javac +ENV_COMPILER_VER=17 +ENV_BUILD_CONFIG=Release +ENV_OS=JVM +ENV_ARCH=jdk-17.0.2.jdk \ No newline at end of file diff --git a/lib/vdyp-back/src/main/resources/logging.properties b/lib/vdyp-back/src/main/resources/logging.properties new file mode 100644 index 000000000..759315742 --- /dev/null +++ b/lib/vdyp-back/src/main/resources/logging.properties @@ -0,0 +1,3 @@ +handlers = java.util.logging.ConsoleHandler +java.util.logging.ConsoleHandler.level = FINE +.level = FINE diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java new file mode 100644 index 000000000..14c74412a --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java @@ -0,0 +1,20 @@ +package ca.bc.gov.nrs.vdyp.application; + +public enum Pass { + INITIALIZE("Perform Initiation activities"), + OPEN_FILES("Open the stand data files"), + PROCESS_STANDS("Process stands"), + MULTIPLE_STANDS("Allow multiple polygons"), + CLOSE_FILES("Close data files"), + ADDITIONAL_BASE_AREA_CRITERIA("Impose additional base area criteria"); + + final String description; + + private Pass(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java new file mode 100644 index 000000000..1f85437f2 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java @@ -0,0 +1,103 @@ +package ca.bc.gov.nrs.vdyp.application; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.bc.gov.nrs.vdyp.io.FileResolver; +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; + +/** + * + * The overall algorithm of a VDYP Application. + * + */ +public abstract class Processor { + + private static final Logger logger = LoggerFactory.getLogger(Processor.class); + + /** + * Initialize Processor + * + * @param inputFileResolver + * @param outputFileResolver + * @param controlFileNames + * + * @throws IOException + * @throws ResourceParseException + * @throws ProcessingException + */ + public void run( + FileResolver inputFileResolver, FileResolver outputFileResolver, List controlFileNames, + Set vdypPassSet + ) throws IOException, ResourceParseException, ProcessingException { + + logPass(vdypPassSet); + + // Load the control map + Map controlMap = new HashMap<>(); + + var parser = getControlFileParser(); + + for (var controlFileName : controlFileNames) { + logger.info("Resolving and parsing {}", controlFileName); + + try (var is = inputFileResolver.resolveForInput(controlFileName)) { + Path controlFilePath = inputFileResolver.toPath(controlFileName).getParent(); + FileSystemFileResolver relativeResolver = new FileSystemFileResolver(controlFilePath); + + parser.parse(is, relativeResolver, controlMap); + } + } + + process(vdypPassSet, controlMap, Optional.of(outputFileResolver)); + } + + /** + * Get a parser for the control file for this application + * + * @return + */ + protected abstract BaseControlParser getControlFileParser(); + + /** + * @return all possible values of the pass enum + */ + protected Set getAllPasses() { + return EnumSet.allOf(Pass.class); + }; + + /** + * Log the settings of the pass set + * + * @param vdypPassSet + */ + protected void logPass(Set active) { + logger.atInfo().addArgument(active).setMessage("VDYPPASS: {}").log(); + for (var pass : getAllPasses()) { + String activeIndicator = active.contains(pass) ? "☑" : "☐"; + logger.atDebug().addArgument(activeIndicator).addArgument(pass.toString()).addArgument(pass.getDescription()); + } + } + + /** + * Implements + * + * @param outputFileResolver + * + * @throws ProcessingException + */ + public abstract void process( + Set vdypPassSet, Map controlMap, Optional outputFileResolver + ) throws ProcessingException; +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java new file mode 100644 index 000000000..0d9f48803 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java @@ -0,0 +1,122 @@ +package ca.bc.gov.nrs.vdyp.application; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import java.util.logging.LogManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver; + +public abstract class VdypProcessingApplication extends VdypApplication { + + @SuppressWarnings("java:S106") + protected static void initLogging(Class klazz) { + try { + LogManager.getLogManager().readConfiguration( + klazz.getClassLoader().getResourceAsStream("logging.properties") + ); + } catch (SecurityException | IOException e) { + System.err.println("Unable to configure logging system"); + } + } + + static final Logger logger = LoggerFactory.getLogger(VdypProcessingApplication.class); + + public abstract String getDefaultControlFileName(); + + protected abstract Processor getProcessor(); + + public static final int CONFIG_LOAD_ERROR_EXIT = 1; + public static final int PROCESSING_ERROR_EXIT = 2; + public static final int NO_ERROR_EXIT = 0; + + protected Set getAllPasses() { + return EnumSet.allOf(Pass.class); + } + + protected VdypProcessingApplication() { + super(); + } + + @SuppressWarnings("java:S106") // Using System.out for direct, console based user interaction. + public int run(final String... args) { + return run(System.out, System.in, args); + } + + public int run(final PrintStream os, final InputStream is, final String... args) { + logVersionInformation(); + + final List controlFileNames; + + try { + if (args.length == 0) { + controlFileNames = getControlFileNamesFromUser(os, is); + } else { + controlFileNames = Arrays.asList(args); + } + } catch (Exception ex) { + logger.error("Error during initialization", ex); + return CONFIG_LOAD_ERROR_EXIT; + } + + try { + Processor processor = getProcessor(); + + processor.run(new FileSystemFileResolver(), new FileSystemFileResolver(), controlFileNames, getAllPasses()); + + System.err.println("Blah"); + + } catch (Exception ex) { + logger.error("Error during processing", ex); + return PROCESSING_ERROR_EXIT; + } + + return NO_ERROR_EXIT; + + } + + public List getControlFileNamesFromUser(final PrintStream os, final InputStream is) throws IOException { + final String defaultFilename = getDefaultControlFileName(); + List controlFileNames; + os.print( + MessageFormat.format( + "Enter name of control file (or RETURN for {1}) or *name for both): ", + defaultFilename + ) + ); + + controlFileNames = new ArrayList<>(); + try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { + String userResponse = br.readLine(); + if (userResponse.length() == 0) { + controlFileNames.add(defaultFilename); + } else { + if (userResponse.startsWith("*")) { + controlFileNames.add(defaultFilename); + userResponse = userResponse.substring(1); + } + controlFileNames.addAll(Arrays.asList(userResponse.split("\s+"))); + } + + } + return controlFileNames; + } + + private void logVersionInformation() { + logger.info("{} {}", RESOURCE_SHORT_VERSION, RESOURCE_VERSION_DATE); + logger.info("{} Ver:{} {}", RESOURCE_BINARY_NAME, RESOURCE_SHORT_VERSION, RESOURCE_VERSION_DATE); + logger.info("VDYP7 Support Ver: {}", AVERSION); + } + +} \ No newline at end of file diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java new file mode 100644 index 000000000..a122d8e52 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java @@ -0,0 +1,236 @@ +package ca.bc.gov.nrs.vdyp.application; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.EnumSet; +import java.util.List; + +import org.easymock.EasyMock; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.io.FileResolver; + +class VdypProcessingApplicationTest { + + @Nested + class Run { + + VdypProcessingApplication app; + Processor processor; + + @BeforeEach + void init() { + processor = EasyMock.createMock(Processor.class); + + app = new VdypProcessingApplication() { + + @Override + public String getDefaultControlFileName() { + return "default.ctl"; + } + + @Override + protected Processor getProcessor() { + return processor; + } + + @Override + public VdypApplicationIdentifier getId() { + fail(); + return null; + } + }; + + } + + @Test + void testCommandLineControlNoError() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("\n".getBytes()); + + processor.run( + EasyMock.isA(FileResolver.class), + EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument.ctl")), + EasyMock.eq(EnumSet.allOf(Pass.class)) + ); + EasyMock.expectLastCall().once(); + + EasyMock.replay(processor); + + int result = app.run(outPrint, input, "argument.ctl"); + + assertThat(result, is(0)); + + EasyMock.verify(processor); + } + + @Test + void testMultipleCommandLineControlNoError() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("\n".getBytes()); + + processor.run( + EasyMock.isA(FileResolver.class), + EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument1.ctl", "argument2.ctl")), + EasyMock.eq(EnumSet.allOf(Pass.class)) + ); + EasyMock.expectLastCall().once(); + + EasyMock.replay(processor); + + int result = app.run(outPrint, input, "argument1.ctl", "argument2.ctl"); + + assertThat(result, is(0)); + + EasyMock.verify(processor); + } + + @Test + void testConsoleInputControlNoError() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("alternate1.ctl alternate2.ctl\n".getBytes()); + + processor.run( + EasyMock.isA(FileResolver.class), + EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("alternate1.ctl", "alternate2.ctl")), + EasyMock.eq(EnumSet.allOf(Pass.class)) + ); + EasyMock.expectLastCall().once(); + + EasyMock.replay(processor); + + int result = app.run(outPrint, input); + + assertThat(result, is(0)); + + EasyMock.verify(processor); + } + + @Test + void testErrorGettingControlFileNames() throws Exception { + PrintStream outPrint = null; + InputStream input = null; + + EasyMock.replay(processor); + + int result = app.run(outPrint, input); + + assertThat(result, is(1)); + + EasyMock.verify(processor); + } + + @Test + void testErrorWhileProcessing() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("\n".getBytes()); + + processor.run( + EasyMock.isA(FileResolver.class), + EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument.ctl")), + EasyMock.eq(EnumSet.allOf(Pass.class)) + ); + EasyMock.expectLastCall().andThrow(new ProcessingException("Test")).once(); + + EasyMock.replay(processor); + + int result = app.run(outPrint, input, "argument.ctl"); + + assertThat(result, is(2)); + + EasyMock.verify(processor); + } + } + + @Nested + class GetControlFileNamesFromUser { + + VdypProcessingApplication app; + + @BeforeEach + void init() { + app = new VdypProcessingApplication() { + + @Override + public String getDefaultControlFileName() { + return "default.ctl"; + } + + @Override + protected Processor getProcessor() { + fail(); + return null; + } + + @Override + public VdypApplicationIdentifier getId() { + fail(); + return null; + } + + }; + } + + @Test + void testJustDefault() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("\n".getBytes()); + var result = app.getControlFileNamesFromUser(outPrint, input); + assertThat(result, Matchers.contains("default.ctl")); + } + + @Test + void testOneEntry() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("alternate.ctl\n".getBytes()); + var result = app.getControlFileNamesFromUser(outPrint, input); + assertThat(result, Matchers.contains("alternate.ctl")); + } + + @Test + void testTwoEntries() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("alternate1.ctl alternate2.ctl\n".getBytes()); + var result = app.getControlFileNamesFromUser(outPrint, input); + assertThat(result, Matchers.contains("alternate1.ctl", "alternate2.ctl")); + } + + @Test + void testOneEntryPlusDefault() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("*alternate.ctl\n".getBytes()); + var result = app.getControlFileNamesFromUser(outPrint, input); + assertThat(result, Matchers.contains("default.ctl", "alternate.ctl")); + } + + @Test + void testTwoEntriesPlusDefault() throws Exception { + var outBytes = new ByteArrayOutputStream(); + var outPrint = new PrintStream(outBytes); + var input = new ByteArrayInputStream("*alternate1.ctl alternate2.ctl\n".getBytes()); + var result = app.getControlFileNamesFromUser(outPrint, input); + assertThat(result, Matchers.contains("default.ctl", "alternate1.ctl", "alternate2.ctl")); + } + } +} diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java index 8c5a5e830..6c35cde2d 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java @@ -213,6 +213,7 @@ protected void check(Collection errors) { @Override protected VriLayer doBuild() { + // We want the reciprocal of the forest fraction. When we output at the other end the derived values are multiplied by the forest fraction canceling this out. float multiplier = 100f / percentAvailable.orElse(100f); VriLayer result = new VriLayer( polygonIdentifier.get(), // From a4ae14a616ee3e75247dc1542ad91d8130039f6a Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 4 Oct 2024 11:14:19 -0700 Subject: [PATCH 02/45] Adapting forward code for back --- .../java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java | 5 ++--- .../main/java/ca/bc/gov/nrs/vdyp/application/Processor.java | 2 +- .../gov/nrs/vdyp/application/VdypProcessingApplication.java | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java index db74d8d91..f249cc42f 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/VdypBackApplication.java @@ -3,11 +3,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ca.bc.gov.nrs.vdyp.application.Processor; import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.application.VdypProcessingApplication; -public class VdypBackApplication extends VdypProcessingApplication { +public class VdypBackApplication extends VdypProcessingApplication { static { initLogging(VdypBackApplication.class); @@ -24,7 +23,7 @@ public static void main(final String... args) { } @Override - protected Processor getProcessor() { + protected BackProcessor getProcessor() { return null; // TODO } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java index 1f85437f2..569413ca4 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java @@ -75,7 +75,7 @@ public void run( */ protected Set getAllPasses() { return EnumSet.allOf(Pass.class); - }; + } /** * Log the settings of the pass set diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java index 0d9f48803..2a5ec2b15 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java @@ -18,7 +18,7 @@ import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver; -public abstract class VdypProcessingApplication extends VdypApplication { +public abstract class VdypProcessingApplication

extends VdypApplication { @SuppressWarnings("java:S106") protected static void initLogging(Class klazz) { @@ -35,7 +35,7 @@ protected static void initLogging(Class klazz) { public abstract String getDefaultControlFileName(); - protected abstract Processor getProcessor(); + protected abstract P getProcessor(); public static final int CONFIG_LOAD_ERROR_EXIT = 1; public static final int PROCESSING_ERROR_EXIT = 2; From 0e6bf39fa913066f6cc675de00a33381a77e380e Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 4 Oct 2024 13:08:08 -0700 Subject: [PATCH 03/45] Add python cach directory to git ignore list --- tools/src/python/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/src/python/.gitignore diff --git a/tools/src/python/.gitignore b/tools/src/python/.gitignore new file mode 100644 index 000000000..bee8a64b7 --- /dev/null +++ b/tools/src/python/.gitignore @@ -0,0 +1 @@ +__pycache__ From dc35b80f6866abee8c78145f7d8fb7c1410f1912 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 4 Oct 2024 13:06:25 -0700 Subject: [PATCH 04/45] Back Prep --- lib/vdyp-back/pom.xml | 9 +- .../nrs/vdyp/back/BackProcessingEngine.java | 24 + .../bc/gov/nrs/vdyp/back/BackProcessor.java | 29 ++ .../BackLayerProcessingState.java | 43 ++ .../processing_state/BackProcessingState.java | 110 +++++ .../vdyp/application/ProcessingEngine.java | 5 + .../gov/nrs/vdyp/processing_state/Bank.java | 465 ++++++++++++++++++ .../LayerProcessingState.java | 157 ++++++ .../processing_state/ProcessingState.java | 111 +++++ .../VdypProcessingApplicationTest.java | 10 +- lib/vdyp-forward/pom.xml | 34 +- 11 files changed, 980 insertions(+), 17 deletions(-) create mode 100644 lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java create mode 100644 lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java create mode 100644 lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java create mode 100644 lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java diff --git a/lib/vdyp-back/pom.xml b/lib/vdyp-back/pom.xml index 73bd8d8be..49b081646 100644 --- a/lib/vdyp-back/pom.xml +++ b/lib/vdyp-back/pom.xml @@ -25,7 +25,7 @@ vdyp-forward 0.0.1-SNAPSHOT - + ca.bc.gov.nrs.vdyp vdyp-common @@ -33,6 +33,13 @@ test-jar test + + ca.bc.gov.nrs.vdyp + vdyp-forward + ${project.version} + test-jar + test + org.apache.commons diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java new file mode 100644 index 000000000..8f1ce44a1 --- /dev/null +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -0,0 +1,24 @@ +package ca.bc.gov.nrs.vdyp.back; + +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.application.StandProcessingException; +import ca.bc.gov.nrs.vdyp.back.processing_state.BackProcessingState; + +public class BackProcessingEngine extends ProcessingEngine { + + /** + * + * @throws StandProcessingException + */ + // BACKPREP + void prepare(BackProcessingState state) throws ProcessingException { + + state.setBaseAreaVeteran( + state.getVeteranLayerProcessingState() + .map(vetState -> vetState.getBank().basalAreas[0][0 + 1]) + ); + + + } +} diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java new file mode 100644 index 000000000..f97ffabb6 --- /dev/null +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java @@ -0,0 +1,29 @@ +package ca.bc.gov.nrs.vdyp.back; + +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import ca.bc.gov.nrs.vdyp.application.Pass; +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.application.Processor; +import ca.bc.gov.nrs.vdyp.io.FileResolver; +import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; + +public class BackProcessor extends Processor { + + @Override + protected BaseControlParser getControlFileParser() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void process( + Set vdypPassSet, Map controlMap, Optional outputFileResolver + ) throws ProcessingException { + // TODO Auto-generated method stub + + } + +} diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java new file mode 100644 index 000000000..de5b52a22 --- /dev/null +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java @@ -0,0 +1,43 @@ +package ca.bc.gov.nrs.vdyp.back.processing_state; + +import java.util.function.Predicate; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; + +public class BackLayerProcessingState extends + LayerProcessingState { + + protected BackLayerProcessingState( + ProcessingState ps, VdypPolygon polygon, + LayerType subjectLayerType + ) throws ProcessingException { + super(ps, polygon, subjectLayerType); + // TODO Auto-generated constructor stub + } + + @Override + protected Predicate getBankFilter() { + // TODO Auto-generated method stub + return x -> true; + } + + @Override + protected void applyCompatibilityVariables(VdypSpecies species, int i) { + // TODO Auto-generated method stub + + } + + @Override + protected VdypLayer updateLayerFromBank() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java new file mode 100644 index 000000000..fc1cd4523 --- /dev/null +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java @@ -0,0 +1,110 @@ +package ca.bc.gov.nrs.vdyp.back.processing_state; + +import java.util.Map; +import java.util.Optional; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; + +public class BackProcessingState extends ProcessingState { + + Optional baseAreaVeteran = Optional.empty(); // BACK1/BAV + + private static final String COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY = "CompatibilityVariablesSet can be set once only"; + private static final String UNSET_CV_VOLUMES = "unset cvVolumes"; + private static final String UNSET_CV_BASAL_AREAS = "unset cvBasalAreas"; + + // Compatibility Variables - LCV1 & LCVS + private boolean areCompatibilityVariablesSet = false; + + private MatrixMap2[] cvVolume; + private Map[] cvBasalArea; + private Map[] cvQuadraticMeanDiameter; + private Map[] cvPrimaryLayerSmall; + + public BackProcessingState(Map controlMap) throws ProcessingException { + super(controlMap); + // TODO Auto-generated constructor stub + } + + @Override + public ForwardResolvedControlMap resolveControlMap(Map controlMap) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected BackLayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) + throws ProcessingException { + return new BackLayerProcessingState(this, polygon, layer.getLayerType()); + } + + public void setBaseAreaVeteran(Optional baseAreaVeteran) { + this.baseAreaVeteran = baseAreaVeteran; + } + + public void setBaseAreaVeteran(float baseAreaVeteran) { + this.baseAreaVeteran = Optional.of(baseAreaVeteran); + } + + public Optional getBaseAreaVeteran() { + return baseAreaVeteran; + } + + public void setCompatibilityVariableDetails( + MatrixMap2[] cvVolume, + Map[] cvBasalArea, + Map[] cvQuadraticMeanDiameter, + Map[] cvPrimaryLayerSmall + ) { + if (areCompatibilityVariablesSet) { + throw new IllegalStateException(COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY); + } + + this.cvVolume = cvVolume; + this.cvBasalArea = cvBasalArea; + this.cvQuadraticMeanDiameter = cvQuadraticMeanDiameter; + this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; + + areCompatibilityVariablesSet = true; + } + + public float getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_VOLUMES); + } + + return cvVolume[speciesIndex].get(uc, volumeVariable); + } + + public float getCVBasalArea(int speciesIndex, UtilizationClass uc) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvBasalArea[speciesIndex].get(uc); + } + + public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvQuadraticMeanDiameter[speciesIndex].get(uc); + } + + public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvPrimaryLayerSmall[speciesIndex].get(variable); + } +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java new file mode 100644 index 000000000..225e00074 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java @@ -0,0 +1,5 @@ +package ca.bc.gov.nrs.vdyp.application; + +public class ProcessingEngine { + +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java new file mode 100644 index 000000000..364abf310 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java @@ -0,0 +1,465 @@ +package ca.bc.gov.nrs.vdyp.processing_state; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; +import ca.bc.gov.nrs.vdyp.model.BecDefinition; +import ca.bc.gov.nrs.vdyp.model.Sp64DistributionSet; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.VdypEntity; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypSite; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; + +public class Bank { + + @SuppressWarnings("unused") + private static final Logger logger = LoggerFactory.getLogger(Bank.class); + + private static final int N_UTILIZATION_CLASSES = UtilizationClass.values().length; + + private final VdypLayer layer; + private final BecDefinition becZone; + + /** + * The number of species in the state. Note that all arrays have this value plus one elements in them; the element + * at index 0 is unused for the species values* and contains the default utilization in the Utilization values. + * + * (*) except: siteCurveNumbers[0] is used to store the site curve of the primary species. + */ + private int nSpecies; // BANK1 NSPB + private int[] indices; + + // Species information + + public final String[/* nSpecies + 1 */] speciesNames; // BANK2 SP0B + public final Sp64DistributionSet[/* nSpecies + 1 */] sp64Distributions; // BANK2 SP64DISTB + public final float[/* nSpecies + 1 */] siteIndices; // BANK3 SIB + public final float[/* nSpecies + 1 */] dominantHeights; // BANK3 HDB + public final float[/* nSpecies + 1 */] ageTotals; // BANK3 AGETOTB + public final float[/* nSpecies + 1 */] yearsAtBreastHeight; // BANK3 AGEBHB + public final float[/* nSpecies + 1 */] yearsToBreastHeight; // BANK3 YTBHB + public final int[/* nSpecies + 1 */] siteCurveNumbers; // BANK3 SCNB + public final int[/* nSpecies + 1 */] speciesIndices; // BANK1 ISPB + public final float[/* nSpecies + 1 */] percentagesOfForestedLand; // BANK1 PCTB + + // Utilization information, per Species + + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] basalAreas; // BANK1 BAB. Units: m^2/hectare + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] closeUtilizationVolumes; // BANK1 VOLCUB + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecay; // BANK1 VOL_DB + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecayAndWastage; // BANK1 VOL_DW_B + public final float[/* nSpecies + 1, including 0 */][/* uc -1 and 0 only */] loreyHeights; // BANK1 HLB + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] quadMeanDiameters; // BANK1 DQB + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] treesPerHectare; // BANK1 TPHB + public final float[/* nSpecies + 1, including 0 */][/* all ucs */] wholeStemVolumes; // BANK1 VOLWSB + + public Bank(VdypLayer layer, BecDefinition becZone, Predicate retainCriteria) { + + this.layer = layer; + this.becZone = becZone; + + List speciesToRetain = layer.getSpecies().values().stream().filter(s -> retainCriteria.test(s)) + .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); + + this.nSpecies = speciesToRetain.size(); + this.indices = IntStream.range(1, nSpecies + 1).toArray(); + + // In the following, index 0 is unused + speciesNames = new String[nSpecies + 1]; + sp64Distributions = new Sp64DistributionSet[getNSpecies() + 1]; + siteIndices = new float[nSpecies + 1]; + dominantHeights = new float[nSpecies + 1]; + ageTotals = new float[nSpecies + 1]; + yearsAtBreastHeight = new float[nSpecies + 1]; + yearsToBreastHeight = new float[nSpecies + 1]; + siteCurveNumbers = new int[nSpecies + 1]; + speciesIndices = new int[nSpecies + 1]; + percentagesOfForestedLand = new float[nSpecies + 1]; + + // In the following, index 0 is used for the default species utilization + basalAreas = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + closeUtilizationVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + cuVolumesMinusDecay = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + cuVolumesMinusDecayAndWastage = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + loreyHeights = new float[nSpecies + 1][2]; + quadMeanDiameters = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + treesPerHectare = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + wholeStemVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; + + int nextSlot = 1; + for (VdypSpecies s : speciesToRetain) { + transferSpeciesIntoBank(nextSlot++, s); + } + + transferUtilizationSetIntoBank(0, layer); + + // BANKCHK1 - calculate UC All values from components (rather than rely on the values + // provided in the input.) + + setCalculateUtilizationClassAllValues(); + } + + public Bank(Bank source) { + + this.becZone = source.becZone; + this.layer = source.layer; + + this.nSpecies = source.nSpecies; + this.indices = copy(source.indices); + this.speciesNames = copy(source.speciesNames); + this.speciesIndices = copy(source.speciesIndices); + + this.siteCurveNumbers = copy(source.siteCurveNumbers); + this.sp64Distributions = copy(source.sp64Distributions); + + this.ageTotals = copy(source.ageTotals); + this.dominantHeights = copy(source.dominantHeights); + this.percentagesOfForestedLand = copy(source.percentagesOfForestedLand); + this.siteIndices = copy(source.siteIndices); + this.yearsAtBreastHeight = copy(source.yearsAtBreastHeight); + this.yearsToBreastHeight = copy(source.yearsToBreastHeight); + + this.basalAreas = copy(source.basalAreas); + this.closeUtilizationVolumes = copy(source.closeUtilizationVolumes); + this.cuVolumesMinusDecay = copy(source.cuVolumesMinusDecay); + this.cuVolumesMinusDecayAndWastage = copy(source.cuVolumesMinusDecayAndWastage); + this.loreyHeights = copy(source.loreyHeights); + this.quadMeanDiameters = copy(source.quadMeanDiameters); + this.treesPerHectare = copy(source.treesPerHectare); + this.wholeStemVolumes = copy(source.wholeStemVolumes); + } + + public int getNSpecies() { + return nSpecies; + } + + int[] getIndices() { + return indices; + } + + BecDefinition getBecZone() { + return becZone; + } + + /** + * Refresh the values in the bank with an updated version (given) of the layer used to create the bank. The + * modifications cannot include any changes to the set of species, although the details of those species may of + * course change. + * + * @param layer a (presumably modified) version of the layer. + * @throws ProcessingException + */ + void refreshBank(VdypLayer layer) throws ProcessingException { + + if (!this.layer.equals(layer)) { + throw new IllegalArgumentException( + MessageFormat.format( + "One cannot refresh a bank from a" + + " layer ({0}) different from the one used to create the bank ({1})", + this.layer, layer + ) + ); + } + + List species = layer.getSpecies().values().stream() + .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); + + transferUtilizationSetIntoBank(0, layer); + + int nextSlot = 1; + for (VdypSpecies s : species) { + transferSpeciesIntoBank(nextSlot++, s); + } + } + + private void transferSpeciesIntoBank(int index, VdypSpecies species) { + + speciesNames[index] = species.getGenus(); + sp64Distributions[index] = species.getSp64DistributionSet(); + speciesIndices[index] = species.getGenusIndex(); + + species.getSite().ifPresentOrElse(s -> { + siteIndices[index] = s.getSiteIndex().orElse(VdypEntity.MISSING_FLOAT_VALUE); + dominantHeights[index] = s.getHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); + ageTotals[index] = s.getAgeTotal().orElse(VdypEntity.MISSING_FLOAT_VALUE); + yearsToBreastHeight[index] = s.getYearsToBreastHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); + if (ageTotals[index] != VdypEntity.MISSING_FLOAT_VALUE + && yearsToBreastHeight[index] != VdypEntity.MISSING_FLOAT_VALUE) { + yearsAtBreastHeight[index] = ageTotals[index] - yearsToBreastHeight[index]; + } else { + yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; + } + siteCurveNumbers[index] = s.getSiteCurveNumber().orElse(VdypEntity.MISSING_INTEGER_VALUE); + // percentForestedLand is output-only and so not assigned here. + }, () -> { + siteIndices[index] = VdypEntity.MISSING_FLOAT_VALUE; + dominantHeights[index] = VdypEntity.MISSING_FLOAT_VALUE; + ageTotals[index] = VdypEntity.MISSING_FLOAT_VALUE; + yearsToBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; + yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; + siteCurveNumbers[index] = VdypEntity.MISSING_INTEGER_VALUE; + }); + + transferUtilizationSetIntoBank(index, species); + } + + private void transferUtilizationSetIntoBank(int index, VdypUtilizationHolder uh) { + + for (UtilizationClass uc : UtilizationClass.values()) { + int ucIndex = uc.ordinal(); + basalAreas[index][ucIndex] = uh.getBaseAreaByUtilization().get(uc); + closeUtilizationVolumes[index][ucIndex] = uh.getCloseUtilizationVolumeByUtilization().get(uc); + cuVolumesMinusDecay[index][ucIndex] = uh.getCloseUtilizationVolumeNetOfDecayByUtilization().get(uc); + cuVolumesMinusDecayAndWastage[index][ucIndex] = uh + .getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().get(uc); + if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { + loreyHeights[index][ucIndex] = uh.getLoreyHeightByUtilization().get(uc); + } + quadMeanDiameters[index][ucIndex] = uh.getQuadraticMeanDiameterByUtilization().get(uc); + treesPerHectare[index][ucIndex] = uh.getTreesPerHectareByUtilization().get(uc); + wholeStemVolumes[index][ucIndex] = uh.getWholeStemVolumeByUtilization().get(uc); + } + } + + /** + * For each species, set uc All to the sum of the UC values, UC 7.5 and above only, for the summable values, and + * calculate quad-mean-diameter from these values. + *

+ * For the layer, set uc All values (for summable types) to the sum of those of the individual species and set the + * other uc values to the sum of those of the individual species. Calculate the uc All value for quad-mean-diameter, + * and the uc All and Small value for lorey-height. + */ + private void setCalculateUtilizationClassAllValues() { + + int layerIndex = 0; + int ucAllIndex = UtilizationClass.ALL.ordinal(); + int ucSmallIndex = UtilizationClass.SMALL.ordinal(); + + // Each species + + for (int sp0Index : indices) { + + basalAreas[sp0Index][ucAllIndex] = sumUtilizationClassValues( + basalAreas[sp0Index], UtilizationClass.UTIL_CLASSES + ); + treesPerHectare[sp0Index][ucAllIndex] = sumUtilizationClassValues( + treesPerHectare[sp0Index], UtilizationClass.UTIL_CLASSES + ); + wholeStemVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( + wholeStemVolumes[sp0Index], UtilizationClass.UTIL_CLASSES + ); + closeUtilizationVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( + closeUtilizationVolumes[sp0Index], UtilizationClass.UTIL_CLASSES + ); + cuVolumesMinusDecay[sp0Index][ucAllIndex] = sumUtilizationClassValues( + cuVolumesMinusDecay[sp0Index], UtilizationClass.UTIL_CLASSES + ); + cuVolumesMinusDecayAndWastage[sp0Index][ucAllIndex] = sumUtilizationClassValues( + cuVolumesMinusDecayAndWastage[sp0Index], UtilizationClass.UTIL_CLASSES + ); + + if (basalAreas[sp0Index][ucAllIndex] > 0.0f) { + quadMeanDiameters[sp0Index][ucAllIndex] = BaseAreaTreeDensityDiameter + .quadMeanDiameter(basalAreas[sp0Index][ucAllIndex], treesPerHectare[sp0Index][ucAllIndex]); + } + } + + // Layer + + basalAreas[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues(basalAreas, UtilizationClass.ALL); + treesPerHectare[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( + treesPerHectare, UtilizationClass.ALL + ); + wholeStemVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( + wholeStemVolumes, UtilizationClass.ALL + ); + closeUtilizationVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( + closeUtilizationVolumes, UtilizationClass.ALL + ); + cuVolumesMinusDecay[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( + cuVolumesMinusDecay, UtilizationClass.ALL + ); + cuVolumesMinusDecayAndWastage[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( + cuVolumesMinusDecayAndWastage, UtilizationClass.ALL + ); + + // Calculate the layer's uc All values for quad-mean-diameter and lorey height + + float sumLoreyHeightByBasalAreaSmall = 0.0f; + float sumBasalAreaSmall = 0.0f; + float sumLoreyHeightByBasalAreaAll = 0.0f; + + for (int sp0Index : indices) { + sumLoreyHeightByBasalAreaSmall += loreyHeights[sp0Index][ucSmallIndex] * basalAreas[sp0Index][ucSmallIndex]; + sumBasalAreaSmall += basalAreas[sp0Index][ucSmallIndex]; + sumLoreyHeightByBasalAreaAll += loreyHeights[sp0Index][ucAllIndex] * basalAreas[sp0Index][ucAllIndex]; + } + + if (basalAreas[layerIndex][ucAllIndex] > 0.0f) { + quadMeanDiameters[layerIndex][ucAllIndex] = BaseAreaTreeDensityDiameter + .quadMeanDiameter(basalAreas[layerIndex][ucAllIndex], treesPerHectare[layerIndex][ucAllIndex]); + loreyHeights[layerIndex][ucAllIndex] = sumLoreyHeightByBasalAreaAll / basalAreas[layerIndex][ucAllIndex]; + } + + // Calculate the layer's lorey height uc Small value + + if (sumBasalAreaSmall > 0.0f) { + loreyHeights[layerIndex][ucSmallIndex] = sumLoreyHeightByBasalAreaSmall / sumBasalAreaSmall; + } + + // Finally, set the layer's summable UC values (other than All, which was computed above) to + // the sums of those of each of the species. + + for (UtilizationClass uc : UtilizationClass.ALL_CLASSES) { + basalAreas[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(basalAreas, uc); + treesPerHectare[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(treesPerHectare, uc); + wholeStemVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(wholeStemVolumes, uc); + closeUtilizationVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( + closeUtilizationVolumes, uc + ); + cuVolumesMinusDecay[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(cuVolumesMinusDecay, uc); + cuVolumesMinusDecayAndWastage[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( + cuVolumesMinusDecayAndWastage, uc + ); + } + } + + private float sumUtilizationClassValues(float[] ucValues, List subjects) { + float sum = 0.0f; + + for (UtilizationClass uc : UtilizationClass.values()) { + if (subjects.contains(uc)) { + sum += ucValues[uc.ordinal()]; + } + } + + return sum; + } + + private float sumSpeciesUtilizationClassValues(float[][] ucValues, UtilizationClass uc) { + float sum = 0.0f; + + for (int sp0Index : this.indices) { + sum += ucValues[sp0Index][uc.ordinal()]; + } + + return sum; + } + + /** + * This method copies the Bank contents out to the VdypLayer instance used to create it and returns that. It is a + * relatively expensive operation and should not be called without due consideration. + * + * @return as described + */ + VdypLayer buildLayerFromBank() { + + transferUtilizationsFromBank(0, layer); + + Collection newSpecies = new ArrayList<>(); + for (int i : indices) { + newSpecies.add(transferSpeciesFromBank(i, layer.getSpecies().get(speciesNames[i]))); + } + layer.setSpecies(newSpecies); + + return layer; + } + + private VdypSpecies transferSpeciesFromBank(int index, VdypSpecies species) { + + VdypSpecies newSpecies = VdypSpecies.build(speciesBuilder -> { + speciesBuilder.copy(species); + speciesBuilder.percentGenus(this.percentagesOfForestedLand[index]); + species.getSite().ifPresentOrElse(site -> speciesBuilder.addSite(VdypSite.build(siteBuilder -> { + siteBuilder.copy(site); + siteBuilder.siteGenus(this.speciesNames[index]); + siteBuilder.ageTotal(Utils.optFloat(ageTotals[index])); + siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); + siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); + siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); + siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); + })), () -> { + VdypSite site = VdypSite.build(siteBuilder -> { + siteBuilder.polygonIdentifier(species.getPolygonIdentifier()); + siteBuilder.layerType(species.getLayerType()); + siteBuilder.siteGenus(this.speciesNames[index]); + siteBuilder.ageTotal(Utils.optFloat(this.ageTotals[index])); + siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); + siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); + siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); + siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); + }); + + speciesBuilder.addSite(site); + }); + }); + + transferUtilizationsFromBank(index, newSpecies); + + return newSpecies; + } + + private void transferUtilizationsFromBank(int index, VdypUtilizationHolder uh) { + + for (UtilizationClass uc : UtilizationClass.values()) { + int ucIndex = uc.ordinal(); + uh.getBaseAreaByUtilization().set(uc, basalAreas[index][ucIndex]); + uh.getCloseUtilizationVolumeByUtilization().set(uc, closeUtilizationVolumes[index][ucIndex]); + uh.getCloseUtilizationVolumeNetOfDecayByUtilization().set(uc, cuVolumesMinusDecay[index][ucIndex]); + uh.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() + .set(uc, cuVolumesMinusDecayAndWastage[index][ucIndex]); + if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { + uh.getLoreyHeightByUtilization().set(uc, loreyHeights[index][ucIndex]); + } + uh.getQuadraticMeanDiameterByUtilization().set(uc, quadMeanDiameters[index][ucIndex]); + uh.getTreesPerHectareByUtilization().set(uc, treesPerHectare[index][ucIndex]); + uh.getWholeStemVolumeByUtilization().set(uc, wholeStemVolumes[index][ucIndex]); + } + } + + public Bank copy() { + return new Bank(this); + } + + private String[] copy(String[] a) { + return Arrays.stream(a).toArray(String[]::new); + } + + private int[] copy(int[] a) { + int[] t = new int[a.length]; + + System.arraycopy(a, 0, t, 0, a.length); + + return t; + } + + private float[] copy(float[] a) { + float[] t = new float[a.length]; + + System.arraycopy(a, 0, t, 0, a.length); + + return t; + } + + private float[][] copy(float[][] a) { + return Arrays.stream(a).map(float[]::clone).toArray(float[][]::new); + } + + private Sp64DistributionSet[] copy(Sp64DistributionSet[] sp64Distributions) { + return Arrays.stream(sp64Distributions).map(s -> s == null ? null : s.copy()) + .toArray(Sp64DistributionSet[]::new); + } +} \ No newline at end of file diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java new file mode 100644 index 000000000..79d64fb4d --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -0,0 +1,157 @@ +package ca.bc.gov.nrs.vdyp.processing_state; + +import java.util.Map; +import java.util.function.Predicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.BecDefinition; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; + +public abstract class LayerProcessingState> { + + private static final Logger logger = LoggerFactory.getLogger(LayerProcessingState.class); + + private static final String COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY = "CompatibilityVariablesSet can be set once only"; + private static final String UNSET_CV_VOLUMES = "unset cvVolumes"; + private static final String UNSET_CV_BASAL_AREAS = "unset cvBasalAreas"; + + /** The containing ForwardProcessingState */ + private final ProcessingState ps; + + /** The containing polygon of the layer on which the Processor is operating */ + private final VdypPolygon polygon; + + /** The type of Layer being processed */ + private final LayerType layerType; + + // L1COM1, L1COM4 and L1COM5 - these common blocks mirror BANK1, BANK2 and BANK3 and are initialized + // when copied to "active" in ForwardProcessingEngine. + + /** + * State of the layer during processing. + */ + private Bank bank; + + // Compatibility Variables - LCV1 & LCVS + private boolean areCompatibilityVariablesSet = false; + + private MatrixMap3[] cvVolume; + private MatrixMap2[] cvBasalArea; + private MatrixMap2[] cvQuadraticMeanDiameter; + private Map[] cvPrimaryLayerSmall; + + protected LayerProcessingState(ProcessingState ps, VdypPolygon polygon, LayerType subjectLayerType) + throws ProcessingException { + + this.ps = ps; + this.polygon = polygon; + this.layerType = subjectLayerType; + + BecDefinition becZone = polygon.getBiogeoclimaticZone(); + + this.bank = new Bank( + polygon.getLayers().get(subjectLayerType), becZone, + getBankFilter() + ); + + } + + protected abstract Predicate getBankFilter(); + + public VdypPolygon getPolygon() { + return polygon; + } + + public LayerType getLayerType() { + return layerType; + } + + public BecDefinition getBecZone() { + return bank.getBecZone(); + } + + + public static Logger getLogger() { + return logger; + } + + public ProcessingState getParent() { + return ps; + } + + public Bank getBank() { + return bank; + } + + protected abstract void applyCompatibilityVariables(VdypSpecies species, int i); + + public int getNSpecies() { + return bank.getNSpecies(); + } + + protected abstract VdypLayer updateLayerFromBank(); + + public void setCompatibilityVariableDetails( + MatrixMap3[] cvVolume, + MatrixMap2[] cvBasalArea, + MatrixMap2[] cvQuadraticMeanDiameter, + Map[] cvPrimaryLayerSmall + ) { + if (areCompatibilityVariablesSet) { + throw new IllegalStateException(COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY); + } + + this.cvVolume = cvVolume; + this.cvBasalArea = cvBasalArea; + this.cvQuadraticMeanDiameter = cvQuadraticMeanDiameter; + this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; + + areCompatibilityVariablesSet = true; + } + + public float + getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable, LayerType layerType) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_VOLUMES); + } + + return cvVolume[speciesIndex].get(uc, volumeVariable, layerType); + } + + public float getCVBasalArea(int speciesIndex, UtilizationClass uc, LayerType layerType) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvBasalArea[speciesIndex].get(uc, layerType); + } + + public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc, LayerType layerType) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvQuadraticMeanDiameter[speciesIndex].get(uc, layerType); + } + + public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { + if (!areCompatibilityVariablesSet) { + throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + } + + return cvPrimaryLayerSmall[speciesIndex].get(variable); + } + +} \ No newline at end of file diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java new file mode 100644 index 000000000..ca33f37c8 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java @@ -0,0 +1,111 @@ +package ca.bc.gov.nrs.vdyp.processing_state; + +import java.util.Map; +import java.util.Optional; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.application.RuntimeProcessingException; +import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; +import ca.bc.gov.nrs.vdyp.common.ComputationMethods; +import ca.bc.gov.nrs.vdyp.common.EstimationMethods; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.BecDefinition; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; + +public abstract class ProcessingState> { + + /** The control map defining the context of the execution */ + final RCM controlMap; + + /** The estimators instance used by this engine */ + final EstimationMethods estimators; + + /** The computation instance used by this engine */ + final ComputationMethods computers; + + /** The polygon on which the Processor is currently operating */ + private VdypPolygon polygon; + + /** The processing state of the primary layer of polygon */ + private LS plps; + + /** The processing state of the veteran layer of polygon */ + private Optional vlps; + + protected ProcessingState(Map controlMap) throws ProcessingException { + this.controlMap = resolveControlMap(controlMap); + this.estimators = new EstimationMethods(this.controlMap); + this.computers = new ComputationMethods(estimators, VdypApplicationIdentifier.VDYP_FORWARD); + } + + protected abstract RCM resolveControlMap(Map controlMap); + + protected abstract LS createLayerState(VdypPolygon polygon, VdypLayer layer) throws ProcessingException; + + private LS createLayerStateSafe(VdypPolygon polygon, VdypLayer layer) { + try { + return createLayerState(polygon, layer); + } catch (ProcessingException e) { + throw new RuntimeProcessingException(e); + } + } + + protected Optional getLayer(LayerType type) { + return Optional.ofNullable(polygon).flatMap(p -> Optional.ofNullable(p.getLayers().get(type))); + } + + public void setPolygon(VdypPolygon polygon) throws ProcessingException { + + this.polygon = polygon; + + this.plps = createLayerState( + polygon, getLayer(LayerType.PRIMARY) + .orElseThrow(() -> new IllegalStateException("No primary layer")) + ); + try { + this.vlps = getLayer(LayerType.VETERAN) + .map(layer -> createLayerStateSafe(polygon, layer)); + } catch (RuntimeProcessingException e) { + throw e.getCause(); + } + } + + /** @return the current polygon */ + public VdypPolygon getCurrentPolygon() { + return polygon; + } + + /** @return the compact form of the current polygon's identifier. Shortcut. */ + public String getCompactPolygonIdentifier() { + return polygon.getPolygonIdentifier().toStringCompact(); + } + + /** @return the starting year of the current polygon. Shortcut. */ + public int getCurrentStartingYear() { + return polygon.getPolygonIdentifier().getYear(); + } + + /** @return the bec zone of the current polygon. Shortcut. */ + public BecDefinition getCurrentBecZone() { + return polygon.getBiogeoclimaticZone(); + } + + public LS getPrimaryLayerProcessingState() { + return plps; + } + + public Optional getVeteranLayerProcessingState() { + return vlps; + } + + public VdypPolygon updatePolygon() { + + polygon.getLayers().put(LayerType.PRIMARY, plps.updateLayerFromBank()); + vlps.ifPresent(vlps -> polygon.getLayers().put(LayerType.VETERAN, vlps.updateLayerFromBank())); + + return polygon; + } +} \ No newline at end of file diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java index a122d8e52..8ea42c2b4 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java @@ -193,7 +193,7 @@ void testJustDefault() throws Exception { var outBytes = new ByteArrayOutputStream(); var outPrint = new PrintStream(outBytes); var input = new ByteArrayInputStream("\n".getBytes()); - var result = app.getControlFileNamesFromUser(outPrint, input); + List result = app.getControlFileNamesFromUser(outPrint, input); assertThat(result, Matchers.contains("default.ctl")); } @@ -202,7 +202,7 @@ void testOneEntry() throws Exception { var outBytes = new ByteArrayOutputStream(); var outPrint = new PrintStream(outBytes); var input = new ByteArrayInputStream("alternate.ctl\n".getBytes()); - var result = app.getControlFileNamesFromUser(outPrint, input); + List result = app.getControlFileNamesFromUser(outPrint, input); assertThat(result, Matchers.contains("alternate.ctl")); } @@ -211,7 +211,7 @@ void testTwoEntries() throws Exception { var outBytes = new ByteArrayOutputStream(); var outPrint = new PrintStream(outBytes); var input = new ByteArrayInputStream("alternate1.ctl alternate2.ctl\n".getBytes()); - var result = app.getControlFileNamesFromUser(outPrint, input); + List result = app.getControlFileNamesFromUser(outPrint, input); assertThat(result, Matchers.contains("alternate1.ctl", "alternate2.ctl")); } @@ -220,7 +220,7 @@ void testOneEntryPlusDefault() throws Exception { var outBytes = new ByteArrayOutputStream(); var outPrint = new PrintStream(outBytes); var input = new ByteArrayInputStream("*alternate.ctl\n".getBytes()); - var result = app.getControlFileNamesFromUser(outPrint, input); + List result = app.getControlFileNamesFromUser(outPrint, input); assertThat(result, Matchers.contains("default.ctl", "alternate.ctl")); } @@ -229,7 +229,7 @@ void testTwoEntriesPlusDefault() throws Exception { var outBytes = new ByteArrayOutputStream(); var outPrint = new PrintStream(outBytes); var input = new ByteArrayInputStream("*alternate1.ctl alternate2.ctl\n".getBytes()); - var result = app.getControlFileNamesFromUser(outPrint, input); + List result = app.getControlFileNamesFromUser(outPrint, input); assertThat(result, Matchers.contains("default.ctl", "alternate1.ctl", "alternate2.ctl")); } } diff --git a/lib/vdyp-forward/pom.xml b/lib/vdyp-forward/pom.xml index e46351603..638821348 100644 --- a/lib/vdyp-forward/pom.xml +++ b/lib/vdyp-forward/pom.xml @@ -1,10 +1,11 @@ - + 4.0.0 - + vdyp-forward jar - + Variable Density Yield Project - Forward http://maven.apache.org @@ -13,14 +14,14 @@ vdyp-lib 0.0.1-SNAPSHOT - + ca.bc.gov.nrs.vdyp vdyp-common 0.0.1-SNAPSHOT - + ca.bc.gov.nrs.vdyp vdyp-common @@ -30,8 +31,8 @@ - org.apache.commons - commons-lang3 + org.apache.commons + commons-lang3 @@ -42,7 +43,7 @@ org.slf4j slf4j-jdk14 - + org.junit.jupiter junit-jupiter-api @@ -72,7 +73,7 @@ src/main/resources/application.properties - + src/main/resources true @@ -97,8 +98,19 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + - + From 1082ad49446f04ca5d6e62e7c465de06539962ae Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 17 Oct 2024 13:10:39 -0700 Subject: [PATCH 05/45] Backprep --- .../nrs/vdyp/back/BackProcessingEngine.java | 103 +++++++++++++++++- .../bc/gov/nrs/vdyp/back/BackProcessor.java | 5 +- .../BackLayerProcessingState.java | 4 +- .../processing_state/BackProcessingState.java | 51 +++++++-- .../ca/bc/gov/nrs/vdyp/application/Pass.java | 7 +- .../gov/nrs/vdyp/application/Processor.java | 46 ++++++-- .../VdypProcessingApplication.java | 11 +- .../nrs/vdyp/io/write/VdypOutputWriter.java | 9 +- .../LayerProcessingState.java | 6 +- .../processing_state/ProcessingState.java | 15 ++- .../VdypProcessingApplicationTest.java | 24 ++-- .../nrs/vdyp/forward/ForwardProcessor.java | 102 +++-------------- .../vdyp/forward/VdypForwardApplication.java | 12 +- ...wardProcessorCheckpointGenerationTest.java | 13 +-- .../forward/ForwardProcessorEndToEndTest.java | 12 +- .../ForwardProcessorZipOutputStreamTest.java | 12 +- .../bc/gov/nrs/vdyp/vri/model/VriLayer.java | 3 +- 17 files changed, 250 insertions(+), 185 deletions(-) diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java index 8f1ce44a1..30ef4433e 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -1,24 +1,121 @@ package ca.bc.gov.nrs.vdyp.back; +import static java.lang.Math.max; +import static java.lang.Math.min; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.application.StandProcessingException; import ca.bc.gov.nrs.vdyp.back.processing_state.BackProcessingState; +import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.Region; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; public class BackProcessingEngine extends ProcessingEngine { /** - * + * * @throws StandProcessingException */ // BACKPREP void prepare(BackProcessingState state) throws ProcessingException { + // Copy the basal area for the veteran layer if it exists to the polygon state + state.setBaseAreaVeteran( - state.getVeteranLayerProcessingState() - .map(vetState -> vetState.getBank().basalAreas[0][0 + 1]) + state.getVeteranLayerProcessingState().map(vetState -> vetState.getBank().basalAreas[0][0 + 1]) ); + // Copy slices of the compatibility variables for the primary layer to the polygon state + + var primaryState = state.getPrimaryLayerProcessingState(); + + int specCount = primaryState.getNSpecies(); + + @SuppressWarnings("unchecked") + MatrixMap2[] cvVolume = new MatrixMap2[specCount + 1]; + @SuppressWarnings("unchecked") + Map[] cvBasalArea = new Map[specCount + 1]; + @SuppressWarnings("unchecked") + Map[] cvQuadraticMeanDiameter = new Map[specCount + 1]; + @SuppressWarnings("unchecked") + Map[] cvPrimaryLayerSmall = new Map[specCount + 1]; + + for (int i = 0; i < primaryState.getNSpecies(); i++) { + final int specIndex = i + 1; + cvVolume[specIndex] = new MatrixMap2Impl<>( + List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), + (uc, vv) -> primaryState.getCVVolume(specIndex, uc, vv, LayerType.PRIMARY) + ); + + cvBasalArea[specIndex] = new EnumMap<>(UtilizationClass.class); + cvQuadraticMeanDiameter[specIndex] = new EnumMap<>(UtilizationClass.class); + cvPrimaryLayerSmall[specIndex] = new EnumMap<>(UtilizationClassVariable.class); + + for (var uc : UtilizationClass.values()) { + cvBasalArea[specIndex].put(uc, primaryState.getCVBasalArea(specIndex, uc, LayerType.PRIMARY)); + cvQuadraticMeanDiameter[specIndex] + .put(uc, primaryState.getCVQuadraticMeanDiameter(specIndex, uc, LayerType.PRIMARY)); + } + + for (var ucv : UtilizationClassVariable.values()) { + cvPrimaryLayerSmall[specIndex].put(ucv, primaryState.getCVSmall(specIndex, ucv)); + } + + } + + state.setCompatibilityVariableDetails(cvVolume, cvBasalArea, cvQuadraticMeanDiameter, cvPrimaryLayerSmall); + + Bank primaryBank = state.getPrimaryLayerProcessingState().getBank(); + Region polygonRegion = state.getCurrentBecZone().getRegion(); + + ComponentSizeLimits[] limits = new ComponentSizeLimits[primaryState.getNSpecies() + 1]; + float[] finalDiameters = new float[primaryState.getNSpecies() + 1]; + + int ucIndexAll = UtilizationClass.ALL.ordinal(); // Intentionally use ordinal instead of index. + + finalDiameters[0] = primaryBank.quadMeanDiameters[0][ucIndexAll]; + for (int i = 0; i < primaryState.getNSpecies(); i++) { + final int specIndex = i + 1; + + finalDiameters[specIndex] = primaryBank.quadMeanDiameters[specIndex][0]; + + final var originalLimits = state.getEstimators() + .getLimitsForHeightAndDiameter(primaryBank.speciesNames[specIndex], polygonRegion); + + final float specQuadMeanDiameter = primaryBank.quadMeanDiameters[specIndex][ucIndexAll]; + final float specLoreyHeight = primaryBank.loreyHeights[specIndex][ucIndexAll]; + + final float quadMeanDiameterLoreyHeightRatio = specQuadMeanDiameter / specLoreyHeight; + + final float loreyHeightMaximum = max(originalLimits.loreyHeightMaximum(), specLoreyHeight); + final float quadMeanDiameterMaximum = max(originalLimits.quadMeanDiameterMaximum(), specQuadMeanDiameter); + + final float minQuadMeanDiameterLoreyHeightRatio = min( + originalLimits.minQuadMeanDiameterLoreyHeightRatio(), quadMeanDiameterLoreyHeightRatio + ); + final float maxQuadMeanDiameterLoreyHeightRatio = max( + originalLimits.maxQuadMeanDiameterLoreyHeightRatio(), quadMeanDiameterLoreyHeightRatio + ); + + limits[specIndex] = new ComponentSizeLimits( + loreyHeightMaximum, quadMeanDiameterMaximum, minQuadMeanDiameterLoreyHeightRatio, + maxQuadMeanDiameterLoreyHeightRatio + ); + + } + state.setLimits(limits); + state.setFinalQuadMeanDiameters(finalDiameters); } } diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java index f97ffabb6..f69fad3c4 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessor.java @@ -3,12 +3,14 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.application.Processor; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; public class BackProcessor extends Processor { @@ -20,7 +22,8 @@ protected BaseControlParser getControlFileParser() { @Override public void process( - Set vdypPassSet, Map controlMap, Optional outputFileResolver + Set vdypPassSet, Map controlMap, Optional outputFileResolver, + Predicate polygonFilter ) throws ProcessingException { // TODO Auto-generated method stub diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java index de5b52a22..823759fbe 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java @@ -11,8 +11,8 @@ import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; -public class BackLayerProcessingState extends - LayerProcessingState { +public class BackLayerProcessingState + extends LayerProcessingState { protected BackLayerProcessingState( ProcessingState ps, VdypPolygon polygon, diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java index fc1cd4523..f09a3e9ef 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java @@ -1,10 +1,14 @@ package ca.bc.gov.nrs.vdyp.back.processing_state; +import java.text.MessageFormat; import java.util.Map; import java.util.Optional; +import java.util.function.Supplier; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; +import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMapImpl; +import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; @@ -18,8 +22,12 @@ public class BackProcessingState extends ProcessingState baseAreaVeteran = Optional.empty(); // BACK1/BAV private static final String COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY = "CompatibilityVariablesSet can be set once only"; - private static final String UNSET_CV_VOLUMES = "unset cvVolumes"; - private static final String UNSET_CV_BASAL_AREAS = "unset cvBasalAreas"; + private static final Supplier UNSET_CV_VOLUMES = unset("cvVolumes"); + private static final Supplier UNSET_CV_BASAL_AREAS = unset("cvBasalAreas"); + private static final Supplier UNSET_LIMITS = unset("per species limits"); + private static final Supplier UNSET_FINAL_QUAD_MEAN_DIAMETER = unset( + "final quadratic mean diameters" + ); // Compatibility Variables - LCV1 & LCVS private boolean areCompatibilityVariablesSet = false; @@ -29,6 +37,9 @@ public class BackProcessingState extends ProcessingState[] cvQuadraticMeanDiameter; private Map[] cvPrimaryLayerSmall; + private Optional speciesLimits = Optional.empty(); + private Optional finalQuadraticMeanDiameters = Optional.empty(); + public BackProcessingState(Map controlMap) throws ProcessingException { super(controlMap); // TODO Auto-generated constructor stub @@ -36,8 +47,7 @@ public BackProcessingState(Map controlMap) throws ProcessingExce @Override public ForwardResolvedControlMap resolveControlMap(Map controlMap) { - // TODO Auto-generated method stub - return null; + return new ForwardResolvedControlMapImpl(controlMap); } @Override @@ -59,8 +69,7 @@ public Optional getBaseAreaVeteran() { } public void setCompatibilityVariableDetails( - MatrixMap2[] cvVolume, - Map[] cvBasalArea, + MatrixMap2[] cvVolume, Map[] cvBasalArea, Map[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall ) { @@ -78,7 +87,7 @@ public void setCompatibilityVariableDetails( public float getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable) { if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_VOLUMES); + throw UNSET_CV_VOLUMES.get(); } return cvVolume[speciesIndex].get(uc, volumeVariable); @@ -86,7 +95,7 @@ public float getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable v public float getCVBasalArea(int speciesIndex, UtilizationClass uc) { if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + throw UNSET_CV_BASAL_AREAS.get(); } return cvBasalArea[speciesIndex].get(uc); @@ -94,7 +103,7 @@ public float getCVBasalArea(int speciesIndex, UtilizationClass uc) { public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc) { if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + throw UNSET_CV_BASAL_AREAS.get(); } return cvQuadraticMeanDiameter[speciesIndex].get(uc); @@ -102,9 +111,31 @@ public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc) { public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); + throw UNSET_CV_BASAL_AREAS.get(); } return cvPrimaryLayerSmall[speciesIndex].get(variable); } + + public void setLimits(ComponentSizeLimits[] limits) { + this.speciesLimits = Optional.of(limits); + } + + public ComponentSizeLimits getLimits(int speciesIndex) { + return this.speciesLimits.orElseThrow(UNSET_LIMITS)[speciesIndex]; + } + + public float getFinalQuadraticMeanDiameter(int speciesIndex) { + return this.finalQuadraticMeanDiameters.orElseThrow(UNSET_FINAL_QUAD_MEAN_DIAMETER)[speciesIndex]; + } + + protected static Supplier unset(final String field) { + final String message = MessageFormat.format("unset {0}", field); + return () -> new IllegalStateException(message); + } + + public void setFinalQuadMeanDiameters(float[] finalDiameters) { + finalQuadraticMeanDiameters = Optional.of(finalDiameters); + } + } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java index 14c74412a..16e60d9ee 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Pass.java @@ -1,11 +1,8 @@ package ca.bc.gov.nrs.vdyp.application; public enum Pass { - INITIALIZE("Perform Initiation activities"), - OPEN_FILES("Open the stand data files"), - PROCESS_STANDS("Process stands"), - MULTIPLE_STANDS("Allow multiple polygons"), - CLOSE_FILES("Close data files"), + INITIALIZE("Perform Initiation activities"), OPEN_FILES("Open the stand data files"), + PROCESS_STANDS("Process stands"), MULTIPLE_STANDS("Allow multiple polygons"), CLOSE_FILES("Close data files"), ADDITIONAL_BASE_AREA_CRITERIA("Impose additional base area criteria"); final String description; diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java index 569413ca4..347373fde 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java @@ -8,6 +8,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,12 +17,24 @@ 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.model.VdypPolygon; /** * - * The overall algorithm of a VDYP Application. + * The common code for the algorithmic part of a forward or backward growth predictor * + * VDYPPASS IN/OUT I*4(10) Major Control Functions + *

    + *
  1. IN Perform Initiation activities? (0=No, 1=Yes) + *
  2. IN Open the stand data files (0=No, 1=Yes) + *
  3. IN Process stands (0=No, 1=Yes) + *
  4. IN Allow multiple polygons (0=No, 1=Yes) (Subset of stand processing. May limit to 1 stand) + *
+ * + * @author Michael Junkin, Vivid Solutions + * @author Kevin Smith, Vivid Solutions */ + public abstract class Processor { private static final Logger logger = LoggerFactory.getLogger(Processor.class); @@ -65,13 +78,13 @@ public void run( /** * Get a parser for the control file for this application - * + * * @return */ protected abstract BaseControlParser getControlFileParser(); /** - * @return all possible values of the pass enum + * @return all values of the pass enum applicable for this processor */ protected Set getAllPasses() { return EnumSet.allOf(Pass.class); @@ -79,25 +92,42 @@ protected Set getAllPasses() { /** * Log the settings of the pass set - * + * * @param vdypPassSet */ protected void logPass(Set active) { logger.atInfo().addArgument(active).setMessage("VDYPPASS: {}").log(); for (var pass : getAllPasses()) { String activeIndicator = active.contains(pass) ? "☑" : "☐"; - logger.atDebug().addArgument(activeIndicator).addArgument(pass.toString()).addArgument(pass.getDescription()); + logger.atDebug().addArgument(activeIndicator).addArgument(pass.toString()) + .addArgument(pass.getDescription()); } } /** - * Implements + * Implements the process * - * @param outputFileResolver + * @param vdypPassSet Phases of the process to implement + * @param controlMap Control map to configure the process + * @param outputFileResolver File resolver for the output + */ + public void + process(Set vdypPassSet, Map controlMap, Optional outputFileResolver) + throws ProcessingException { + process(vdypPassSet, controlMap, outputFileResolver, p -> true); + } + + /** + * Implements the process * + * @param vdypPassSet Phases of the process to implement + * @param controlMap Control map to configure the process + * @param outputFileResolver File resolver for the output + * @param polygonFilter A polygon will be processed if and only if this returns true for that polygon * @throws ProcessingException */ public abstract void process( - Set vdypPassSet, Map controlMap, Optional outputFileResolver + Set vdypPassSet, Map controlMap, Optional outputFileResolver, + Predicate polygonFilter ) throws ProcessingException; } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java index 2a5ec2b15..0e700731a 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java @@ -23,9 +23,8 @@ public abstract class VdypProcessingApplication

extends Vdy @SuppressWarnings("java:S106") protected static void initLogging(Class klazz) { try { - LogManager.getLogManager().readConfiguration( - klazz.getClassLoader().getResourceAsStream("logging.properties") - ); + LogManager.getLogManager() + .readConfiguration(klazz.getClassLoader().getResourceAsStream("logging.properties")); } catch (SecurityException | IOException e) { System.err.println("Unable to configure logging system"); } @@ -90,10 +89,8 @@ public List getControlFileNamesFromUser(final PrintStream os, final Inpu final String defaultFilename = getDefaultControlFileName(); List controlFileNames; os.print( - MessageFormat.format( - "Enter name of control file (or RETURN for {1}) or *name for both): ", - defaultFilename - ) + MessageFormat + .format("Enter name of control file (or RETURN for {1}) or *name for both): ", defaultFilename) ); controlFileNames = new ArrayList<>(); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriter.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriter.java index 88d5095e2..e6fe8e70d 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriter.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriter.java @@ -165,8 +165,9 @@ public void writePolygonWithSpeciesAndUtilization(VdypPolygon polygon) throws IO // The original VDYP7 system performs this task at this location, storing the result in // a separate COMMON. Here, we store the result in the Polygon, knowing that the originally // calculated values are not being used. - sortedLayers.stream() - .forEach(l -> calculateCuVolumeLessDecayWastageBreakage(controlMap, l, polygon.getBiogeoclimaticZone())); + sortedLayers.stream().forEach( + l -> calculateCuVolumeLessDecayWastageBreakage(controlMap, l, polygon.getBiogeoclimaticZone()) + ); for (var layer : sortedLayers) { writeUtilization(polygon, layer, layer); @@ -182,7 +183,9 @@ public void writePolygonWithSpeciesAndUtilization(VdypPolygon polygon) throws IO writeUtilizationEndRecord(polygon); } - static void calculateCuVolumeLessDecayWastageBreakage(ResolvedControlMap controlMap, VdypLayer layer, BecDefinition bec) { + static void calculateCuVolumeLessDecayWastageBreakage( + ResolvedControlMap controlMap, VdypLayer layer, BecDefinition bec + ) { // Technically, BreakageEquationGroups are not required. If missing, it will not be // possible for this method to do its work; but, it's still not an error. diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index 79d64fb4d..1ba0d8e4d 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -61,10 +61,7 @@ protected LayerProcessingState(ProcessingState ps, VdypPolygon polygo BecDefinition becZone = polygon.getBiogeoclimaticZone(); - this.bank = new Bank( - polygon.getLayers().get(subjectLayerType), becZone, - getBankFilter() - ); + this.bank = new Bank(polygon.getLayers().get(subjectLayerType), becZone, getBankFilter()); } @@ -82,7 +79,6 @@ public BecDefinition getBecZone() { return bank.getBecZone(); } - public static Logger getLogger() { return logger; } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java index ca33f37c8..34bb91d59 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java @@ -8,7 +8,6 @@ import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.common.ComputationMethods; import ca.bc.gov.nrs.vdyp.common.EstimationMethods; -import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; @@ -23,6 +22,14 @@ public abstract class ProcessingState new IllegalStateException("No primary layer")) + polygon, getLayer(LayerType.PRIMARY).orElseThrow(() -> new IllegalStateException("No primary layer")) ); try { - this.vlps = getLayer(LayerType.VETERAN) - .map(layer -> createLayerStateSafe(polygon, layer)); + this.vlps = getLayer(LayerType.VETERAN).map(layer -> createLayerStateSafe(polygon, layer)); } catch (RuntimeProcessingException e) { throw e.getCause(); } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java index 8ea42c2b4..2ff829e0d 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java @@ -59,10 +59,8 @@ void testCommandLineControlNoError() throws Exception { var input = new ByteArrayInputStream("\n".getBytes()); processor.run( - EasyMock.isA(FileResolver.class), - EasyMock.isA(FileResolver.class), - EasyMock.eq(List.of("argument.ctl")), - EasyMock.eq(EnumSet.allOf(Pass.class)) + EasyMock.isA(FileResolver.class), EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument.ctl")), EasyMock.eq(EnumSet.allOf(Pass.class)) ); EasyMock.expectLastCall().once(); @@ -82,10 +80,8 @@ void testMultipleCommandLineControlNoError() throws Exception { var input = new ByteArrayInputStream("\n".getBytes()); processor.run( - EasyMock.isA(FileResolver.class), - EasyMock.isA(FileResolver.class), - EasyMock.eq(List.of("argument1.ctl", "argument2.ctl")), - EasyMock.eq(EnumSet.allOf(Pass.class)) + EasyMock.isA(FileResolver.class), EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument1.ctl", "argument2.ctl")), EasyMock.eq(EnumSet.allOf(Pass.class)) ); EasyMock.expectLastCall().once(); @@ -105,10 +101,8 @@ void testConsoleInputControlNoError() throws Exception { var input = new ByteArrayInputStream("alternate1.ctl alternate2.ctl\n".getBytes()); processor.run( - EasyMock.isA(FileResolver.class), - EasyMock.isA(FileResolver.class), - EasyMock.eq(List.of("alternate1.ctl", "alternate2.ctl")), - EasyMock.eq(EnumSet.allOf(Pass.class)) + EasyMock.isA(FileResolver.class), EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("alternate1.ctl", "alternate2.ctl")), EasyMock.eq(EnumSet.allOf(Pass.class)) ); EasyMock.expectLastCall().once(); @@ -142,10 +136,8 @@ void testErrorWhileProcessing() throws Exception { var input = new ByteArrayInputStream("\n".getBytes()); processor.run( - EasyMock.isA(FileResolver.class), - EasyMock.isA(FileResolver.class), - EasyMock.eq(List.of("argument.ctl")), - EasyMock.eq(EnumSet.allOf(Pass.class)) + EasyMock.isA(FileResolver.class), EasyMock.isA(FileResolver.class), + EasyMock.eq(List.of("argument.ctl")), EasyMock.eq(EnumSet.allOf(Pass.class)) ); EasyMock.expectLastCall().andThrow(new ProcessingException("Test")).once(); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java index 32f2c6d35..2a949f00a 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java @@ -1,9 +1,6 @@ package ca.bc.gov.nrs.vdyp.forward; import java.io.IOException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -12,11 +9,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.application.Processor; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.io.FileResolver; -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.write.VdypOutputWriter; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; @@ -48,87 +46,12 @@ * * * @author Michael Junkin, Vivid Solutions + * @author Kevin Smith, Vivid Solutions */ -public class ForwardProcessor { +public class ForwardProcessor extends Processor { private static final Logger logger = LoggerFactory.getLogger(ForwardProcessor.class); - /** - * Initialize VdypForwardProcessor - * - * @param inputFileResolver - * @param outputFileResolver - * @param controlFileNames - * - * @throws IOException - * @throws ResourceParseException - * @throws ProcessingException - */ - void run( - FileResolver inputFileResolver, FileResolver outputFileResolver, List controlFileNames, - Set vdypPassSet - ) throws IOException, ResourceParseException, ProcessingException { - run(inputFileResolver, outputFileResolver, controlFileNames, vdypPassSet, (p) -> true); - } - - /** - * Initialize VdypForwardProcessor - * - * @param inputFileResolver - * @param outputFileResolver - * @param controlFileNames - * - * @throws IOException - * @throws ResourceParseException - * @throws ProcessingException - */ - void run( - FileResolver inputFileResolver, FileResolver outputFileResolver, List controlFileNames, - Set vdypPassSet, Predicate polygonFilter - ) throws IOException, ResourceParseException, ProcessingException { - - logger.info("VDYPPASS: {}", vdypPassSet); - logger.debug("VDYPPASS(1): Perform Initiation activities?"); - logger.debug("VDYPPASS(2): Open the stand data files"); - logger.debug("VDYPPASS(3): Process stands"); - logger.debug("VDYPPASS(4): Allow multiple polygons"); - logger.debug("VDYPPASS(5): Close data files"); - logger.debug(" "); - - // Load the control map - Map controlMap = new HashMap<>(); - - var parser = new ForwardControlParser(); - - for (var controlFileName : controlFileNames) { - logger.info("Resolving and parsing {}", controlFileName); - - try (var is = inputFileResolver.resolveForInput(controlFileName)) { - Path controlFilePath = inputFileResolver.toPath(controlFileName).getParent(); - FileSystemFileResolver relativeResolver = new FileSystemFileResolver(controlFilePath); - - parser.parse(is, relativeResolver, controlMap); - } - } - - process(vdypPassSet, controlMap, Optional.of(outputFileResolver), polygonFilter); - } - - /** - * Implements VDYP_SUB. - * - * @param vdypPassSet the set of stages (passes) to be executed - * @param controlMap parsed control map - * @param outputFileResolver optional file resolver that, if present, locates output files. - * - * @throws ProcessingException - */ - public void process( - Set vdypPassSet, Map controlMap, Optional outputFileResolver - ) throws ProcessingException { - process(vdypPassSet, controlMap, outputFileResolver, (p) -> true); - } - /** * Implements VDYP_SUB, excluding all polygons that don't pass the given polygonFilter. * @@ -139,15 +62,17 @@ public void process( * * @throws ProcessingException */ + @Override + public void process( - Set vdypPassSet, Map controlMap, Optional outputFileResolver, + Set vdypPassSet, Map controlMap, Optional outputFileResolver, Predicate polygonFilter ) throws ProcessingException { logger.info("Beginning processing with given configuration"); int maxPoly = 0; - if (vdypPassSet.contains(ForwardPass.PASS_1)) { + if (vdypPassSet.contains(Pass.INITIALIZE)) { Object maxPolyValue = controlMap.get(ControlKey.MAX_NUM_POLY.name()); if (maxPolyValue != null) { maxPoly = (Integer) maxPolyValue; @@ -156,12 +81,12 @@ public void process( logger.debug("MaxPoly: {}", maxPoly); - if (vdypPassSet.contains(ForwardPass.PASS_2)) { + if (vdypPassSet.contains(Pass.OPEN_FILES)) { // input files are already opened // TODO: open output files } - if (vdypPassSet.contains(ForwardPass.PASS_3)) { + if (vdypPassSet.contains(Pass.PROCESS_STANDS)) { Optional outputWriter = Optional.empty(); @@ -210,4 +135,9 @@ public void process( }); } } + + @Override + protected BaseControlParser getControlFileParser() { + return new ForwardControlParser(); + } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/VdypForwardApplication.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/VdypForwardApplication.java index d149f83a7..06b506be0 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/VdypForwardApplication.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/VdypForwardApplication.java @@ -1,17 +1,11 @@ package ca.bc.gov.nrs.vdyp.forward; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_1; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_2; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_3; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_4; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_5; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.logging.LogManager; @@ -19,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.VdypApplication; import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver; @@ -42,7 +37,8 @@ public class VdypForwardApplication extends VdypApplication { public static final String DEFAULT_VDYP_CONTROL_FILE_NAME = "vdyp.ctr"; - private static Set vdypPassSet = new HashSet<>(Arrays.asList(PASS_1, PASS_2, PASS_3, PASS_4, PASS_5)); + private static Set vdypPassSet = EnumSet + .of(Pass.INITIALIZE, Pass.OPEN_FILES, Pass.PROCESS_STANDS, Pass.MULTIPLE_STANDS); public static void main(final String... args) { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java index e05e46e7e..6df64445a 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java @@ -1,18 +1,11 @@ package ca.bc.gov.nrs.vdyp.forward; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_1; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_2; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_3; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_4; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_5; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -22,6 +15,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; @@ -40,7 +34,8 @@ class ForwardProcessorCheckpointGenerationTest { @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(ForwardProcessorCheckpointGenerationTest.class); - private static Set vdypPassSet = new HashSet<>(Arrays.asList(PASS_1, PASS_2, PASS_3, PASS_4, PASS_5)); + private static Set vdypPassSet = EnumSet + .of(Pass.INITIALIZE, Pass.OPEN_FILES, Pass.PROCESS_STANDS, Pass.MULTIPLE_STANDS); @Test void test() throws IOException, ResourceParseException, ProcessingException { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java index 5beb3e84e..3e16c1843 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java @@ -1,10 +1,5 @@ package ca.bc.gov.nrs.vdyp.forward; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_1; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_2; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_3; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_4; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_5; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -12,10 +7,9 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -25,6 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; @@ -52,7 +47,8 @@ class ForwardProcessorEndToEndTest { @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(ForwardProcessorEndToEndTest.class); - private static Set vdypPassSet = new HashSet<>(Arrays.asList(PASS_1, PASS_2, PASS_3, PASS_4, PASS_5)); + private static Set vdypPassSet = EnumSet + .of(Pass.INITIALIZE, Pass.OPEN_FILES, Pass.PROCESS_STANDS, Pass.MULTIPLE_STANDS); private int nEquals = 0; private int nWithin1Percent = 0; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorZipOutputStreamTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorZipOutputStreamTest.java index f50182e46..4f4790f6e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorZipOutputStreamTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorZipOutputStreamTest.java @@ -1,10 +1,5 @@ package ca.bc.gov.nrs.vdyp.forward; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_1; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_2; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_3; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_4; -import static ca.bc.gov.nrs.vdyp.forward.ForwardPass.PASS_5; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,8 +8,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashSet; +import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.zip.ZipFile; @@ -24,6 +18,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.ZipOutputFileResolver; @@ -34,7 +29,8 @@ class ForwardProcessorZipOutputStreamTest { private static final Logger logger = LoggerFactory.getLogger(ForwardProcessorZipOutputStreamTest.class); - private static Set vdypPassSet = new HashSet<>(Arrays.asList(PASS_1, PASS_2, PASS_3, PASS_4, PASS_5)); + private static Set vdypPassSet = EnumSet + .of(Pass.INITIALIZE, Pass.OPEN_FILES, Pass.PROCESS_STANDS, Pass.MULTIPLE_STANDS); @TempDir Path outputFilesLocation; diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java index 6c35cde2d..3d8e3692b 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriLayer.java @@ -213,7 +213,8 @@ protected void check(Collection errors) { @Override protected VriLayer doBuild() { - // We want the reciprocal of the forest fraction. When we output at the other end the derived values are multiplied by the forest fraction canceling this out. + // We want the reciprocal of the forest fraction. When we output at the other end the derived values are + // multiplied by the forest fraction canceling this out. float multiplier = 100f / percentAvailable.orElse(100f); VriLayer result = new VriLayer( polygonIdentifier.get(), // From f8f55aa187bdc11ad320a985f369e48c1883dce4 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 17 Oct 2024 13:44:06 -0700 Subject: [PATCH 06/45] Consolidate Bank in common --- .../gov/nrs/vdyp/processing_state/Bank.java | 8 +- .../java/ca/bc/gov/nrs/vdyp/forward/Bank.java | 465 ------------------ .../vdyp/forward/ForwardProcessingEngine.java | 1 + .../vdyp/forward/LayerProcessingState.java | 1 + .../ca/bc/gov/nrs/vdyp/forward/BankTest.java | 1 + .../forward/Grow10StoreSpeciesDetails.java | 1 + 6 files changed, 8 insertions(+), 469 deletions(-) delete mode 100644 lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java index 364abf310..781a7a70f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java @@ -146,11 +146,11 @@ public int getNSpecies() { return nSpecies; } - int[] getIndices() { + public int[] getIndices() { return indices; } - BecDefinition getBecZone() { + public BecDefinition getBecZone() { return becZone; } @@ -162,7 +162,7 @@ BecDefinition getBecZone() { * @param layer a (presumably modified) version of the layer. * @throws ProcessingException */ - void refreshBank(VdypLayer layer) throws ProcessingException { + public void refreshBank(VdypLayer layer) throws ProcessingException { if (!this.layer.equals(layer)) { throw new IllegalArgumentException( @@ -365,7 +365,7 @@ private float sumSpeciesUtilizationClassValues(float[][] ucValues, UtilizationCl * * @return as described */ - VdypLayer buildLayerFromBank() { + public VdypLayer buildLayerFromBank() { transferUtilizationsFromBank(0, layer); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java deleted file mode 100644 index 7728c2a4f..000000000 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java +++ /dev/null @@ -1,465 +0,0 @@ -package ca.bc.gov.nrs.vdyp.forward; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.common.Utils; -import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; -import ca.bc.gov.nrs.vdyp.model.Sp64DistributionSet; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.VdypEntity; -import ca.bc.gov.nrs.vdyp.model.VdypLayer; -import ca.bc.gov.nrs.vdyp.model.VdypSite; -import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; - -class Bank { - - @SuppressWarnings("unused") - private static final Logger logger = LoggerFactory.getLogger(Bank.class); - - private static final int N_UTILIZATION_CLASSES = UtilizationClass.values().length; - - private final VdypLayer layer; - private final BecDefinition becZone; - - /** - * The number of species in the state. Note that all arrays have this value plus one elements in them; the element - * at index 0 is unused for the species values* and contains the default utilization in the Utilization values. - * - * (*) except: siteCurveNumbers[0] is used to store the site curve of the primary species. - */ - private int nSpecies; // BANK1 NSPB - private int[] indices; - - // Species information - - public final String[/* nSpecies + 1 */] speciesNames; // BANK2 SP0B - public final Sp64DistributionSet[/* nSpecies + 1 */] sp64Distributions; // BANK2 SP64DISTB - public final float[/* nSpecies + 1 */] siteIndices; // BANK3 SIB - public final float[/* nSpecies + 1 */] dominantHeights; // BANK3 HDB - public final float[/* nSpecies + 1 */] ageTotals; // BANK3 AGETOTB - public final float[/* nSpecies + 1 */] yearsAtBreastHeight; // BANK3 AGEBHB - public final float[/* nSpecies + 1 */] yearsToBreastHeight; // BANK3 YTBHB - public final int[/* nSpecies + 1 */] siteCurveNumbers; // BANK3 SCNB - public final int[/* nSpecies + 1 */] speciesIndices; // BANK1 ISPB - public final float[/* nSpecies + 1 */] percentagesOfForestedLand; // BANK1 PCTB - - // Utilization information, per Species - - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] basalAreas; // BANK1 BAB. Units: m^2/hectare - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] closeUtilizationVolumes; // BANK1 VOLCUB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecay; // BANK1 VOL_DB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecayAndWastage; // BANK1 VOL_DW_B - public final float[/* nSpecies + 1, including 0 */][/* uc -1 and 0 only */] loreyHeights; // BANK1 HLB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] quadMeanDiameters; // BANK1 DQB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] treesPerHectare; // BANK1 TPHB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] wholeStemVolumes; // BANK1 VOLWSB - - public Bank(VdypLayer layer, BecDefinition becZone, Predicate retainCriteria) { - - this.layer = layer; - this.becZone = becZone; - - List speciesToRetain = layer.getSpecies().values().stream().filter(s -> retainCriteria.test(s)) - .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); - - this.nSpecies = speciesToRetain.size(); - this.indices = IntStream.range(1, nSpecies + 1).toArray(); - - // In the following, index 0 is unused - speciesNames = new String[nSpecies + 1]; - sp64Distributions = new Sp64DistributionSet[getNSpecies() + 1]; - siteIndices = new float[nSpecies + 1]; - dominantHeights = new float[nSpecies + 1]; - ageTotals = new float[nSpecies + 1]; - yearsAtBreastHeight = new float[nSpecies + 1]; - yearsToBreastHeight = new float[nSpecies + 1]; - siteCurveNumbers = new int[nSpecies + 1]; - speciesIndices = new int[nSpecies + 1]; - percentagesOfForestedLand = new float[nSpecies + 1]; - - // In the following, index 0 is used for the default species utilization - basalAreas = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - closeUtilizationVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - cuVolumesMinusDecay = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - cuVolumesMinusDecayAndWastage = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - loreyHeights = new float[nSpecies + 1][2]; - quadMeanDiameters = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - treesPerHectare = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - wholeStemVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - - int nextSlot = 1; - for (VdypSpecies s : speciesToRetain) { - transferSpeciesIntoBank(nextSlot++, s); - } - - transferUtilizationSetIntoBank(0, layer); - - // BANKCHK1 - calculate UC All values from components (rather than rely on the values - // provided in the input.) - - setCalculateUtilizationClassAllValues(); - } - - public Bank(Bank source) { - - this.becZone = source.becZone; - this.layer = source.layer; - - this.nSpecies = source.nSpecies; - this.indices = copy(source.indices); - this.speciesNames = copy(source.speciesNames); - this.speciesIndices = copy(source.speciesIndices); - - this.siteCurveNumbers = copy(source.siteCurveNumbers); - this.sp64Distributions = copy(source.sp64Distributions); - - this.ageTotals = copy(source.ageTotals); - this.dominantHeights = copy(source.dominantHeights); - this.percentagesOfForestedLand = copy(source.percentagesOfForestedLand); - this.siteIndices = copy(source.siteIndices); - this.yearsAtBreastHeight = copy(source.yearsAtBreastHeight); - this.yearsToBreastHeight = copy(source.yearsToBreastHeight); - - this.basalAreas = copy(source.basalAreas); - this.closeUtilizationVolumes = copy(source.closeUtilizationVolumes); - this.cuVolumesMinusDecay = copy(source.cuVolumesMinusDecay); - this.cuVolumesMinusDecayAndWastage = copy(source.cuVolumesMinusDecayAndWastage); - this.loreyHeights = copy(source.loreyHeights); - this.quadMeanDiameters = copy(source.quadMeanDiameters); - this.treesPerHectare = copy(source.treesPerHectare); - this.wholeStemVolumes = copy(source.wholeStemVolumes); - } - - public int getNSpecies() { - return nSpecies; - } - - int[] getIndices() { - return indices; - } - - BecDefinition getBecZone() { - return becZone; - } - - /** - * Refresh the values in the bank with an updated version (given) of the layer used to create the bank. The - * modifications cannot include any changes to the set of species, although the details of those species may of - * course change. - * - * @param layer a (presumably modified) version of the layer. - * @throws ProcessingException - */ - void refreshBank(VdypLayer layer) throws ProcessingException { - - if (!this.layer.equals(layer)) { - throw new IllegalArgumentException( - MessageFormat.format( - "One cannot refresh a bank from a" - + " layer ({0}) different from the one used to create the bank ({1})", - this.layer, layer - ) - ); - } - - List species = layer.getSpecies().values().stream() - .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); - - transferUtilizationSetIntoBank(0, layer); - - int nextSlot = 1; - for (VdypSpecies s : species) { - transferSpeciesIntoBank(nextSlot++, s); - } - } - - private void transferSpeciesIntoBank(int index, VdypSpecies species) { - - speciesNames[index] = species.getGenus(); - sp64Distributions[index] = species.getSp64DistributionSet(); - speciesIndices[index] = species.getGenusIndex(); - - species.getSite().ifPresentOrElse(s -> { - siteIndices[index] = s.getSiteIndex().orElse(VdypEntity.MISSING_FLOAT_VALUE); - dominantHeights[index] = s.getHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); - ageTotals[index] = s.getAgeTotal().orElse(VdypEntity.MISSING_FLOAT_VALUE); - yearsToBreastHeight[index] = s.getYearsToBreastHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); - if (ageTotals[index] != VdypEntity.MISSING_FLOAT_VALUE - && yearsToBreastHeight[index] != VdypEntity.MISSING_FLOAT_VALUE) { - yearsAtBreastHeight[index] = ageTotals[index] - yearsToBreastHeight[index]; - } else { - yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - } - siteCurveNumbers[index] = s.getSiteCurveNumber().orElse(VdypEntity.MISSING_INTEGER_VALUE); - // percentForestedLand is output-only and so not assigned here. - }, () -> { - siteIndices[index] = VdypEntity.MISSING_FLOAT_VALUE; - dominantHeights[index] = VdypEntity.MISSING_FLOAT_VALUE; - ageTotals[index] = VdypEntity.MISSING_FLOAT_VALUE; - yearsToBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - siteCurveNumbers[index] = VdypEntity.MISSING_INTEGER_VALUE; - }); - - transferUtilizationSetIntoBank(index, species); - } - - private void transferUtilizationSetIntoBank(int index, VdypUtilizationHolder uh) { - - for (UtilizationClass uc : UtilizationClass.values()) { - int ucIndex = uc.ordinal(); - basalAreas[index][ucIndex] = uh.getBaseAreaByUtilization().get(uc); - closeUtilizationVolumes[index][ucIndex] = uh.getCloseUtilizationVolumeByUtilization().get(uc); - cuVolumesMinusDecay[index][ucIndex] = uh.getCloseUtilizationVolumeNetOfDecayByUtilization().get(uc); - cuVolumesMinusDecayAndWastage[index][ucIndex] = uh - .getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().get(uc); - if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { - loreyHeights[index][ucIndex] = uh.getLoreyHeightByUtilization().get(uc); - } - quadMeanDiameters[index][ucIndex] = uh.getQuadraticMeanDiameterByUtilization().get(uc); - treesPerHectare[index][ucIndex] = uh.getTreesPerHectareByUtilization().get(uc); - wholeStemVolumes[index][ucIndex] = uh.getWholeStemVolumeByUtilization().get(uc); - } - } - - /** - * For each species, set uc All to the sum of the UC values, UC 7.5 and above only, for the summable values, and - * calculate quad-mean-diameter from these values. - *

- * For the layer, set uc All values (for summable types) to the sum of those of the individual species and set the - * other uc values to the sum of those of the individual species. Calculate the uc All value for quad-mean-diameter, - * and the uc All and Small value for lorey-height. - */ - private void setCalculateUtilizationClassAllValues() { - - int layerIndex = 0; - int ucAllIndex = UtilizationClass.ALL.ordinal(); - int ucSmallIndex = UtilizationClass.SMALL.ordinal(); - - // Each species - - for (int sp0Index : indices) { - - basalAreas[sp0Index][ucAllIndex] = sumUtilizationClassValues( - basalAreas[sp0Index], UtilizationClass.UTIL_CLASSES - ); - treesPerHectare[sp0Index][ucAllIndex] = sumUtilizationClassValues( - treesPerHectare[sp0Index], UtilizationClass.UTIL_CLASSES - ); - wholeStemVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( - wholeStemVolumes[sp0Index], UtilizationClass.UTIL_CLASSES - ); - closeUtilizationVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( - closeUtilizationVolumes[sp0Index], UtilizationClass.UTIL_CLASSES - ); - cuVolumesMinusDecay[sp0Index][ucAllIndex] = sumUtilizationClassValues( - cuVolumesMinusDecay[sp0Index], UtilizationClass.UTIL_CLASSES - ); - cuVolumesMinusDecayAndWastage[sp0Index][ucAllIndex] = sumUtilizationClassValues( - cuVolumesMinusDecayAndWastage[sp0Index], UtilizationClass.UTIL_CLASSES - ); - - if (basalAreas[sp0Index][ucAllIndex] > 0.0f) { - quadMeanDiameters[sp0Index][ucAllIndex] = BaseAreaTreeDensityDiameter - .quadMeanDiameter(basalAreas[sp0Index][ucAllIndex], treesPerHectare[sp0Index][ucAllIndex]); - } - } - - // Layer - - basalAreas[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues(basalAreas, UtilizationClass.ALL); - treesPerHectare[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - treesPerHectare, UtilizationClass.ALL - ); - wholeStemVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - wholeStemVolumes, UtilizationClass.ALL - ); - closeUtilizationVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - closeUtilizationVolumes, UtilizationClass.ALL - ); - cuVolumesMinusDecay[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecay, UtilizationClass.ALL - ); - cuVolumesMinusDecayAndWastage[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecayAndWastage, UtilizationClass.ALL - ); - - // Calculate the layer's uc All values for quad-mean-diameter and lorey height - - float sumLoreyHeightByBasalAreaSmall = 0.0f; - float sumBasalAreaSmall = 0.0f; - float sumLoreyHeightByBasalAreaAll = 0.0f; - - for (int sp0Index : indices) { - sumLoreyHeightByBasalAreaSmall += loreyHeights[sp0Index][ucSmallIndex] * basalAreas[sp0Index][ucSmallIndex]; - sumBasalAreaSmall += basalAreas[sp0Index][ucSmallIndex]; - sumLoreyHeightByBasalAreaAll += loreyHeights[sp0Index][ucAllIndex] * basalAreas[sp0Index][ucAllIndex]; - } - - if (basalAreas[layerIndex][ucAllIndex] > 0.0f) { - quadMeanDiameters[layerIndex][ucAllIndex] = BaseAreaTreeDensityDiameter - .quadMeanDiameter(basalAreas[layerIndex][ucAllIndex], treesPerHectare[layerIndex][ucAllIndex]); - loreyHeights[layerIndex][ucAllIndex] = sumLoreyHeightByBasalAreaAll / basalAreas[layerIndex][ucAllIndex]; - } - - // Calculate the layer's lorey height uc Small value - - if (sumBasalAreaSmall > 0.0f) { - loreyHeights[layerIndex][ucSmallIndex] = sumLoreyHeightByBasalAreaSmall / sumBasalAreaSmall; - } - - // Finally, set the layer's summable UC values (other than All, which was computed above) to - // the sums of those of each of the species. - - for (UtilizationClass uc : UtilizationClass.ALL_CLASSES) { - basalAreas[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(basalAreas, uc); - treesPerHectare[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(treesPerHectare, uc); - wholeStemVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(wholeStemVolumes, uc); - closeUtilizationVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( - closeUtilizationVolumes, uc - ); - cuVolumesMinusDecay[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(cuVolumesMinusDecay, uc); - cuVolumesMinusDecayAndWastage[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecayAndWastage, uc - ); - } - } - - private float sumUtilizationClassValues(float[] ucValues, List subjects) { - float sum = 0.0f; - - for (UtilizationClass uc : UtilizationClass.values()) { - if (subjects.contains(uc)) { - sum += ucValues[uc.ordinal()]; - } - } - - return sum; - } - - private float sumSpeciesUtilizationClassValues(float[][] ucValues, UtilizationClass uc) { - float sum = 0.0f; - - for (int sp0Index : this.indices) { - sum += ucValues[sp0Index][uc.ordinal()]; - } - - return sum; - } - - /** - * This method copies the Bank contents out to the VdypLayer instance used to create it and returns that. It is a - * relatively expensive operation and should not be called without due consideration. - * - * @return as described - */ - VdypLayer buildLayerFromBank() { - - transferUtilizationsFromBank(0, layer); - - Collection newSpecies = new ArrayList<>(); - for (int i : indices) { - newSpecies.add(transferSpeciesFromBank(i, layer.getSpecies().get(speciesNames[i]))); - } - layer.setSpecies(newSpecies); - - return layer; - } - - private VdypSpecies transferSpeciesFromBank(int index, VdypSpecies species) { - - VdypSpecies newSpecies = VdypSpecies.build(speciesBuilder -> { - speciesBuilder.copy(species); - speciesBuilder.percentGenus(this.percentagesOfForestedLand[index]); - species.getSite().ifPresentOrElse(site -> speciesBuilder.addSite(VdypSite.build(siteBuilder -> { - siteBuilder.copy(site); - siteBuilder.siteGenus(this.speciesNames[index]); - siteBuilder.ageTotal(Utils.optFloat(ageTotals[index])); - siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); - siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); - siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); - siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); - })), () -> { - VdypSite site = VdypSite.build(siteBuilder -> { - siteBuilder.polygonIdentifier(species.getPolygonIdentifier()); - siteBuilder.layerType(species.getLayerType()); - siteBuilder.siteGenus(this.speciesNames[index]); - siteBuilder.ageTotal(Utils.optFloat(this.ageTotals[index])); - siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); - siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); - siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); - siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); - }); - - speciesBuilder.addSite(site); - }); - }); - - transferUtilizationsFromBank(index, newSpecies); - - return newSpecies; - } - - private void transferUtilizationsFromBank(int index, VdypUtilizationHolder uh) { - - for (UtilizationClass uc : UtilizationClass.values()) { - int ucIndex = uc.ordinal(); - uh.getBaseAreaByUtilization().set(uc, basalAreas[index][ucIndex]); - uh.getCloseUtilizationVolumeByUtilization().set(uc, closeUtilizationVolumes[index][ucIndex]); - uh.getCloseUtilizationVolumeNetOfDecayByUtilization().set(uc, cuVolumesMinusDecay[index][ucIndex]); - uh.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() - .set(uc, cuVolumesMinusDecayAndWastage[index][ucIndex]); - if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { - uh.getLoreyHeightByUtilization().set(uc, loreyHeights[index][ucIndex]); - } - uh.getQuadraticMeanDiameterByUtilization().set(uc, quadMeanDiameters[index][ucIndex]); - uh.getTreesPerHectareByUtilization().set(uc, treesPerHectare[index][ucIndex]); - uh.getWholeStemVolumeByUtilization().set(uc, wholeStemVolumes[index][ucIndex]); - } - } - - public Bank copy() { - return new Bank(this); - } - - private String[] copy(String[] a) { - return Arrays.stream(a).toArray(String[]::new); - } - - private int[] copy(int[] a) { - int[] t = new int[a.length]; - - System.arraycopy(a, 0, t, 0, a.length); - - return t; - } - - private float[] copy(float[] a) { - float[] t = new float[a.length]; - - System.arraycopy(a, 0, t, 0, a.length); - - return t; - } - - private float[][] copy(float[][] a) { - return Arrays.stream(a).map(float[]::clone).toArray(float[][]::new); - } - - private Sp64DistributionSet[] copy(Sp64DistributionSet[] sp64Distributions) { - return Arrays.stream(sp64Distributions).map(s -> s == null ? null : s.copy()) - .toArray(Sp64DistributionSet[]::new); - } -} \ No newline at end of file diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index a3425ea5a..2421dc74e 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -66,6 +66,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.si32.site.SiteTool; /** diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java index 60247c49c..9658b589a 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java @@ -18,6 +18,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class LayerProcessingState { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java index 5fcdd4226..acff0c791 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java @@ -26,6 +26,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class BankTest { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java index 151d71b02..ca2fcaa0d 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java @@ -22,6 +22,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParseException; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class Grow10StoreSpeciesDetails { From 03685e93a528fa268a85c3f72fb1578af6f0bac2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 17 Oct 2024 13:44:06 -0700 Subject: [PATCH 07/45] Consolidate Bank in common --- .../gov/nrs/vdyp/processing_state/Bank.java | 8 +- .../nrs/vdyp/processing_state}/BankTest.java | 81 +-- .../nrs/vdyp/test/ProcessingTestUtils.java | 182 +++++++ .../java/ca/bc/gov/nrs/vdyp/forward/Bank.java | 465 ------------------ .../vdyp/forward/ForwardProcessingEngine.java | 1 + .../vdyp/forward/LayerProcessingState.java | 1 + .../forward/Grow10StoreSpeciesDetails.java | 1 + .../vdyp/forward/test/ForwardTestUtils.java | 170 ------- 8 files changed, 235 insertions(+), 674 deletions(-) rename lib/{vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward => vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state}/BankTest.java (84%) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/ProcessingTestUtils.java delete mode 100644 lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java index 364abf310..781a7a70f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java @@ -146,11 +146,11 @@ public int getNSpecies() { return nSpecies; } - int[] getIndices() { + public int[] getIndices() { return indices; } - BecDefinition getBecZone() { + public BecDefinition getBecZone() { return becZone; } @@ -162,7 +162,7 @@ BecDefinition getBecZone() { * @param layer a (presumably modified) version of the layer. * @throws ProcessingException */ - void refreshBank(VdypLayer layer) throws ProcessingException { + public void refreshBank(VdypLayer layer) throws ProcessingException { if (!this.layer.equals(layer)) { throw new IllegalArgumentException( @@ -365,7 +365,7 @@ private float sumSpeciesUtilizationClassValues(float[][] ucValues, UtilizationCl * * @return as described */ - VdypLayer buildLayerFromBank() { + public VdypLayer buildLayerFromBank() { transferUtilizationsFromBank(0, layer); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java similarity index 84% rename from lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java rename to lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index 5fcdd4226..fa0b44267 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -1,8 +1,6 @@ -package ca.bc.gov.nrs.vdyp.forward; +package ca.bc.gov.nrs.vdyp.processing_state; -import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.controlMapHasEntry; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -10,44 +8,73 @@ import java.util.List; import java.util.Map; -import org.hamcrest.Matcher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; -import ca.bc.gov.nrs.vdyp.model.GenusDefinitionMap; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; +import ca.bc.gov.nrs.vdyp.test.ProcessingTestUtils; +import ca.bc.gov.nrs.vdyp.test.TestUtils; class BankTest { - private ForwardControlParser parser; private Map controlMap; + private VdypPolygon polygon; - @SuppressWarnings({ "unchecked", "rawtypes" }) @BeforeEach void before() throws IOException, ResourceParseException { - parser = new ForwardControlParser(); - controlMap = ForwardTestUtils.parse(parser, "VDYP.CTR"); - assertThat(controlMap, (Matcher) controlMapHasEntry(ControlKey.SP0_DEF, instanceOf(GenusDefinitionMap.class))); + controlMap = TestUtils.loadControlMap(); + + var bec = Utils.getBec("CDF", controlMap); + + polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.percentAvailable(99f); + pb.biogeoclimaticZone(bec); + pb.forestInventoryZone("A"); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + + lb.addSpecies(sb -> { + sb.genus("B", controlMap); + sb.baseArea(0.4f); + }); + lb.addSpecies(sb -> { + sb.genus("C", controlMap); + sb.baseArea(0.6f); + }); + lb.addSpecies(sb -> { + sb.genus("D", controlMap); + sb.baseArea(10f); + }); + lb.addSpecies(sb -> { + sb.genus("H", controlMap); + sb.baseArea(50f); + }); + lb.addSpecies(sb -> { + sb.genus("S", controlMap); + sb.baseArea(99.9f); + }); + }); + }); + } @Test void testConstruction() throws IOException, ResourceParseException, ProcessingException { - ForwardDataStreamReader reader = new ForwardDataStreamReader(controlMap); - - var polygon = reader.readNextPolygon().orElseThrow(() -> new AssertionError("No polygons defined")); - VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); assertThat(pLayer, notNullValue()); @@ -105,16 +132,12 @@ void testConstruction() throws IOException, ResourceParseException, ProcessingEx @Test void testSetCopy() throws IOException, ResourceParseException, ProcessingException { - ForwardDataStreamReader reader = new ForwardDataStreamReader(controlMap); - - var polygon = reader.readNextPolygon().orElseThrow(() -> new AssertionError("No polygons defined")); - VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); assertThat(pLayer, notNullValue()); Bank bank = new Bank(pLayer, polygon.getBiogeoclimaticZone(), s -> true); - pLayer = ForwardTestUtils.normalizeLayer(pLayer); + pLayer = ProcessingTestUtils.normalizeLayer(pLayer); verifyBankMatchesLayer(bank, pLayer); Bank ppsCopy = bank.copy(); @@ -125,10 +148,6 @@ void testSetCopy() throws IOException, ResourceParseException, ProcessingExcepti @Test void testRemoveSmallLayers() throws IOException, ResourceParseException, ProcessingException { - ForwardDataStreamReader reader = new ForwardDataStreamReader(controlMap); - - var polygon = reader.readNextPolygon().orElseThrow(() -> new AssertionError("No polygons defined")); - VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); assertThat(pLayer, notNullValue()); @@ -154,10 +173,6 @@ void testRemoveSmallLayers() throws IOException, ResourceParseException, Process @Test void testCopyConstructor() throws IOException, ResourceParseException, ProcessingException { - ForwardDataStreamReader reader = new ForwardDataStreamReader(controlMap); - - var polygon = reader.readNextPolygon().orElseThrow(() -> new AssertionError("No polygons defined")); - VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); assertThat(pLayer, notNullValue()); @@ -165,23 +180,19 @@ void testCopyConstructor() throws IOException, ResourceParseException, Processin Bank bankCopy = new Bank(bank); - pLayer = ForwardTestUtils.normalizeLayer(pLayer); + pLayer = ProcessingTestUtils.normalizeLayer(pLayer); verifyBankMatchesLayer(bankCopy, pLayer); } @Test void testLayerUpdate() throws IOException, ResourceParseException, ProcessingException { - ForwardDataStreamReader reader = new ForwardDataStreamReader(controlMap); - - var polygon = reader.readNextPolygon().orElseThrow(() -> new AssertionError("No polygons defined")); - VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); assertThat(pLayer, notNullValue()); Bank bank = new Bank(pLayer, polygon.getBiogeoclimaticZone(), s -> true); - pLayer = ForwardTestUtils.normalizeLayer(pLayer); + pLayer = ProcessingTestUtils.normalizeLayer(pLayer); verifyBankMatchesLayer(bank, pLayer); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/ProcessingTestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/ProcessingTestUtils.java new file mode 100644 index 000000000..568ffdefa --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/ProcessingTestUtils.java @@ -0,0 +1,182 @@ +package ca.bc.gov.nrs.vdyp.test; + +import java.util.List; + +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; + +public class ProcessingTestUtils { + public static VdypLayer normalizeLayer(VdypLayer layer) { + + // Set uc All to the sum of the UC values, UC 7.5 and above only, for the summable + // values, and calculate quad-mean-diameter from these values. + + UtilizationClass ucAll = UtilizationClass.ALL; + UtilizationClass ucSmall = UtilizationClass.SMALL; + + for (var species : layer.getSpecies().values()) { + + species.getBaseAreaByUtilization().set( + ucAll, sumUtilizationClassValues(species.getBaseAreaByUtilization(), UtilizationClass.UTIL_CLASSES) + ); + species.getTreesPerHectareByUtilization().set( + ucAll, + sumUtilizationClassValues(species.getTreesPerHectareByUtilization(), UtilizationClass.UTIL_CLASSES) + ); + species.getWholeStemVolumeByUtilization().set( + ucAll, + sumUtilizationClassValues(species.getWholeStemVolumeByUtilization(), UtilizationClass.UTIL_CLASSES) + ); + species.getCloseUtilizationVolumeByUtilization().set( + ucAll, + sumUtilizationClassValues( + species.getCloseUtilizationVolumeByUtilization(), UtilizationClass.UTIL_CLASSES + ) + ); + species.getCloseUtilizationVolumeNetOfDecayByUtilization().set( + ucAll, + sumUtilizationClassValues( + species.getCloseUtilizationVolumeNetOfDecayByUtilization(), UtilizationClass.UTIL_CLASSES + ) + ); + species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().set( + ucAll, + sumUtilizationClassValues( + species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), + UtilizationClass.UTIL_CLASSES + ) + ); + + if (species.getBaseAreaByUtilization().get(ucAll) > 0.0f) { + species.getQuadraticMeanDiameterByUtilization().set( + ucAll, + BaseAreaTreeDensityDiameter.quadMeanDiameter( + species.getBaseAreaByUtilization().get(ucAll), + species.getTreesPerHectareByUtilization().get(ucAll) + ) + ); + } + } + + // Set the layer's uc All values (for summable types) to the sum of those of the + // individual species. + + layer.getBaseAreaByUtilization() + .set(ucAll, sumSpeciesUtilizationClassValues(layer, "BaseArea", UtilizationClass.ALL)); + layer.getTreesPerHectareByUtilization() + .set(ucAll, sumSpeciesUtilizationClassValues(layer, "TreesPerHectare", UtilizationClass.ALL)); + layer.getWholeStemVolumeByUtilization() + .set(ucAll, sumSpeciesUtilizationClassValues(layer, "WholeStemVolume", UtilizationClass.ALL)); + layer.getCloseUtilizationVolumeByUtilization() + .set(ucAll, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolume", UtilizationClass.ALL)); + layer.getCloseUtilizationVolumeNetOfDecayByUtilization().set( + ucAll, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecay", UtilizationClass.ALL) + ); + layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().set( + ucAll, + sumSpeciesUtilizationClassValues( + layer, "CloseUtilizationVolumeNetOfDecayAndWaste", UtilizationClass.ALL + ) + ); + + // Calculate the layer's uc All values for quad-mean-diameter and lorey height + + float sumLoreyHeightByBasalAreaSmall = 0.0f; + float sumBasalAreaSmall = 0.0f; + float sumLoreyHeightByBasalAreaAll = 0.0f; + + for (var species : layer.getSpecies().values()) { + sumLoreyHeightByBasalAreaSmall += species.getLoreyHeightByUtilization().get(ucSmall) + * species.getBaseAreaByUtilization().get(ucSmall); + sumBasalAreaSmall += species.getBaseAreaByUtilization().get(ucSmall); + sumLoreyHeightByBasalAreaAll += species.getLoreyHeightByUtilization().get(ucAll) + * species.getBaseAreaByUtilization().get(ucAll); + } + + if (layer.getBaseAreaByUtilization().get(ucAll) > 0.0f) { + layer.getQuadraticMeanDiameterByUtilization().set( + ucAll, + BaseAreaTreeDensityDiameter.quadMeanDiameter( + layer.getBaseAreaByUtilization().get(ucAll), + layer.getTreesPerHectareByUtilization().get(ucAll) + ) + ); + layer.getLoreyHeightByUtilization() + .set(ucAll, sumLoreyHeightByBasalAreaAll / layer.getBaseAreaByUtilization().get(ucAll)); + } + + // Calculate the layer's lorey height uc Small value + + if (sumBasalAreaSmall > 0.0f) { + layer.getLoreyHeightByUtilization().set(ucSmall, sumLoreyHeightByBasalAreaSmall / sumBasalAreaSmall); + } + + // Finally, set the layer's summable UC values (other than All, which was computed above) to + // the sums of those of each of the species. + + for (UtilizationClass uc : UtilizationClass.ALL_CLASSES) { + layer.getBaseAreaByUtilization().set(uc, sumSpeciesUtilizationClassValues(layer, "BaseArea", uc)); + layer.getTreesPerHectareByUtilization() + .set(uc, sumSpeciesUtilizationClassValues(layer, "TreesPerHectare", uc)); + layer.getWholeStemVolumeByUtilization() + .set(uc, sumSpeciesUtilizationClassValues(layer, "WholeStemVolume", uc)); + layer.getCloseUtilizationVolumeByUtilization() + .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolume", uc)); + layer.getCloseUtilizationVolumeNetOfDecayByUtilization() + .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecay", uc)); + layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() + .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecayAndWaste", uc)); + } + + return layer; + } + + private static float sumUtilizationClassValues(UtilizationVector ucValues, List subjects) { + float sum = 0.0f; + + for (UtilizationClass uc : UtilizationClass.values()) { + if (subjects.contains(uc)) { + sum += ucValues.get(uc); + } + } + + return sum; + } + + private static float sumSpeciesUtilizationClassValues(VdypLayer layer, String uvName, UtilizationClass uc) { + float sum = 0.0f; + + for (VdypSpecies species : layer.getSpecies().values()) { + switch (uvName) { + case "CloseUtilizationVolumeNetOfDecayWasteAndBreakage": + sum += species.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization().get(uc); + break; + case "CloseUtilizationVolumeNetOfDecayAndWaste": + sum += species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().get(uc); + break; + case "CloseUtilizationVolumeNetOfDecay": + sum += species.getCloseUtilizationVolumeNetOfDecayByUtilization().get(uc); + break; + case "CloseUtilizationVolume": + sum += species.getCloseUtilizationVolumeByUtilization().get(uc); + break; + case "WholeStemVolume": + sum += species.getWholeStemVolumeByUtilization().get(uc); + break; + case "TreesPerHectare": + sum += species.getTreesPerHectareByUtilization().get(uc); + break; + case "BaseArea": + sum += species.getBaseAreaByUtilization().get(uc); + break; + default: + throw new IllegalStateException(uvName + " is not a known utilization vector name"); + } + } + + return sum; + } +} diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java deleted file mode 100644 index 7728c2a4f..000000000 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/Bank.java +++ /dev/null @@ -1,465 +0,0 @@ -package ca.bc.gov.nrs.vdyp.forward; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.common.Utils; -import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; -import ca.bc.gov.nrs.vdyp.model.Sp64DistributionSet; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.VdypEntity; -import ca.bc.gov.nrs.vdyp.model.VdypLayer; -import ca.bc.gov.nrs.vdyp.model.VdypSite; -import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; - -class Bank { - - @SuppressWarnings("unused") - private static final Logger logger = LoggerFactory.getLogger(Bank.class); - - private static final int N_UTILIZATION_CLASSES = UtilizationClass.values().length; - - private final VdypLayer layer; - private final BecDefinition becZone; - - /** - * The number of species in the state. Note that all arrays have this value plus one elements in them; the element - * at index 0 is unused for the species values* and contains the default utilization in the Utilization values. - * - * (*) except: siteCurveNumbers[0] is used to store the site curve of the primary species. - */ - private int nSpecies; // BANK1 NSPB - private int[] indices; - - // Species information - - public final String[/* nSpecies + 1 */] speciesNames; // BANK2 SP0B - public final Sp64DistributionSet[/* nSpecies + 1 */] sp64Distributions; // BANK2 SP64DISTB - public final float[/* nSpecies + 1 */] siteIndices; // BANK3 SIB - public final float[/* nSpecies + 1 */] dominantHeights; // BANK3 HDB - public final float[/* nSpecies + 1 */] ageTotals; // BANK3 AGETOTB - public final float[/* nSpecies + 1 */] yearsAtBreastHeight; // BANK3 AGEBHB - public final float[/* nSpecies + 1 */] yearsToBreastHeight; // BANK3 YTBHB - public final int[/* nSpecies + 1 */] siteCurveNumbers; // BANK3 SCNB - public final int[/* nSpecies + 1 */] speciesIndices; // BANK1 ISPB - public final float[/* nSpecies + 1 */] percentagesOfForestedLand; // BANK1 PCTB - - // Utilization information, per Species - - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] basalAreas; // BANK1 BAB. Units: m^2/hectare - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] closeUtilizationVolumes; // BANK1 VOLCUB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecay; // BANK1 VOL_DB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] cuVolumesMinusDecayAndWastage; // BANK1 VOL_DW_B - public final float[/* nSpecies + 1, including 0 */][/* uc -1 and 0 only */] loreyHeights; // BANK1 HLB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] quadMeanDiameters; // BANK1 DQB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] treesPerHectare; // BANK1 TPHB - public final float[/* nSpecies + 1, including 0 */][/* all ucs */] wholeStemVolumes; // BANK1 VOLWSB - - public Bank(VdypLayer layer, BecDefinition becZone, Predicate retainCriteria) { - - this.layer = layer; - this.becZone = becZone; - - List speciesToRetain = layer.getSpecies().values().stream().filter(s -> retainCriteria.test(s)) - .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); - - this.nSpecies = speciesToRetain.size(); - this.indices = IntStream.range(1, nSpecies + 1).toArray(); - - // In the following, index 0 is unused - speciesNames = new String[nSpecies + 1]; - sp64Distributions = new Sp64DistributionSet[getNSpecies() + 1]; - siteIndices = new float[nSpecies + 1]; - dominantHeights = new float[nSpecies + 1]; - ageTotals = new float[nSpecies + 1]; - yearsAtBreastHeight = new float[nSpecies + 1]; - yearsToBreastHeight = new float[nSpecies + 1]; - siteCurveNumbers = new int[nSpecies + 1]; - speciesIndices = new int[nSpecies + 1]; - percentagesOfForestedLand = new float[nSpecies + 1]; - - // In the following, index 0 is used for the default species utilization - basalAreas = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - closeUtilizationVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - cuVolumesMinusDecay = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - cuVolumesMinusDecayAndWastage = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - loreyHeights = new float[nSpecies + 1][2]; - quadMeanDiameters = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - treesPerHectare = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - wholeStemVolumes = new float[nSpecies + 1][N_UTILIZATION_CLASSES]; - - int nextSlot = 1; - for (VdypSpecies s : speciesToRetain) { - transferSpeciesIntoBank(nextSlot++, s); - } - - transferUtilizationSetIntoBank(0, layer); - - // BANKCHK1 - calculate UC All values from components (rather than rely on the values - // provided in the input.) - - setCalculateUtilizationClassAllValues(); - } - - public Bank(Bank source) { - - this.becZone = source.becZone; - this.layer = source.layer; - - this.nSpecies = source.nSpecies; - this.indices = copy(source.indices); - this.speciesNames = copy(source.speciesNames); - this.speciesIndices = copy(source.speciesIndices); - - this.siteCurveNumbers = copy(source.siteCurveNumbers); - this.sp64Distributions = copy(source.sp64Distributions); - - this.ageTotals = copy(source.ageTotals); - this.dominantHeights = copy(source.dominantHeights); - this.percentagesOfForestedLand = copy(source.percentagesOfForestedLand); - this.siteIndices = copy(source.siteIndices); - this.yearsAtBreastHeight = copy(source.yearsAtBreastHeight); - this.yearsToBreastHeight = copy(source.yearsToBreastHeight); - - this.basalAreas = copy(source.basalAreas); - this.closeUtilizationVolumes = copy(source.closeUtilizationVolumes); - this.cuVolumesMinusDecay = copy(source.cuVolumesMinusDecay); - this.cuVolumesMinusDecayAndWastage = copy(source.cuVolumesMinusDecayAndWastage); - this.loreyHeights = copy(source.loreyHeights); - this.quadMeanDiameters = copy(source.quadMeanDiameters); - this.treesPerHectare = copy(source.treesPerHectare); - this.wholeStemVolumes = copy(source.wholeStemVolumes); - } - - public int getNSpecies() { - return nSpecies; - } - - int[] getIndices() { - return indices; - } - - BecDefinition getBecZone() { - return becZone; - } - - /** - * Refresh the values in the bank with an updated version (given) of the layer used to create the bank. The - * modifications cannot include any changes to the set of species, although the details of those species may of - * course change. - * - * @param layer a (presumably modified) version of the layer. - * @throws ProcessingException - */ - void refreshBank(VdypLayer layer) throws ProcessingException { - - if (!this.layer.equals(layer)) { - throw new IllegalArgumentException( - MessageFormat.format( - "One cannot refresh a bank from a" - + " layer ({0}) different from the one used to create the bank ({1})", - this.layer, layer - ) - ); - } - - List species = layer.getSpecies().values().stream() - .sorted((s1, s2) -> s1.getGenusIndex() - s2.getGenusIndex()).toList(); - - transferUtilizationSetIntoBank(0, layer); - - int nextSlot = 1; - for (VdypSpecies s : species) { - transferSpeciesIntoBank(nextSlot++, s); - } - } - - private void transferSpeciesIntoBank(int index, VdypSpecies species) { - - speciesNames[index] = species.getGenus(); - sp64Distributions[index] = species.getSp64DistributionSet(); - speciesIndices[index] = species.getGenusIndex(); - - species.getSite().ifPresentOrElse(s -> { - siteIndices[index] = s.getSiteIndex().orElse(VdypEntity.MISSING_FLOAT_VALUE); - dominantHeights[index] = s.getHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); - ageTotals[index] = s.getAgeTotal().orElse(VdypEntity.MISSING_FLOAT_VALUE); - yearsToBreastHeight[index] = s.getYearsToBreastHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE); - if (ageTotals[index] != VdypEntity.MISSING_FLOAT_VALUE - && yearsToBreastHeight[index] != VdypEntity.MISSING_FLOAT_VALUE) { - yearsAtBreastHeight[index] = ageTotals[index] - yearsToBreastHeight[index]; - } else { - yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - } - siteCurveNumbers[index] = s.getSiteCurveNumber().orElse(VdypEntity.MISSING_INTEGER_VALUE); - // percentForestedLand is output-only and so not assigned here. - }, () -> { - siteIndices[index] = VdypEntity.MISSING_FLOAT_VALUE; - dominantHeights[index] = VdypEntity.MISSING_FLOAT_VALUE; - ageTotals[index] = VdypEntity.MISSING_FLOAT_VALUE; - yearsToBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - yearsAtBreastHeight[index] = VdypEntity.MISSING_FLOAT_VALUE; - siteCurveNumbers[index] = VdypEntity.MISSING_INTEGER_VALUE; - }); - - transferUtilizationSetIntoBank(index, species); - } - - private void transferUtilizationSetIntoBank(int index, VdypUtilizationHolder uh) { - - for (UtilizationClass uc : UtilizationClass.values()) { - int ucIndex = uc.ordinal(); - basalAreas[index][ucIndex] = uh.getBaseAreaByUtilization().get(uc); - closeUtilizationVolumes[index][ucIndex] = uh.getCloseUtilizationVolumeByUtilization().get(uc); - cuVolumesMinusDecay[index][ucIndex] = uh.getCloseUtilizationVolumeNetOfDecayByUtilization().get(uc); - cuVolumesMinusDecayAndWastage[index][ucIndex] = uh - .getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().get(uc); - if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { - loreyHeights[index][ucIndex] = uh.getLoreyHeightByUtilization().get(uc); - } - quadMeanDiameters[index][ucIndex] = uh.getQuadraticMeanDiameterByUtilization().get(uc); - treesPerHectare[index][ucIndex] = uh.getTreesPerHectareByUtilization().get(uc); - wholeStemVolumes[index][ucIndex] = uh.getWholeStemVolumeByUtilization().get(uc); - } - } - - /** - * For each species, set uc All to the sum of the UC values, UC 7.5 and above only, for the summable values, and - * calculate quad-mean-diameter from these values. - *

- * For the layer, set uc All values (for summable types) to the sum of those of the individual species and set the - * other uc values to the sum of those of the individual species. Calculate the uc All value for quad-mean-diameter, - * and the uc All and Small value for lorey-height. - */ - private void setCalculateUtilizationClassAllValues() { - - int layerIndex = 0; - int ucAllIndex = UtilizationClass.ALL.ordinal(); - int ucSmallIndex = UtilizationClass.SMALL.ordinal(); - - // Each species - - for (int sp0Index : indices) { - - basalAreas[sp0Index][ucAllIndex] = sumUtilizationClassValues( - basalAreas[sp0Index], UtilizationClass.UTIL_CLASSES - ); - treesPerHectare[sp0Index][ucAllIndex] = sumUtilizationClassValues( - treesPerHectare[sp0Index], UtilizationClass.UTIL_CLASSES - ); - wholeStemVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( - wholeStemVolumes[sp0Index], UtilizationClass.UTIL_CLASSES - ); - closeUtilizationVolumes[sp0Index][ucAllIndex] = sumUtilizationClassValues( - closeUtilizationVolumes[sp0Index], UtilizationClass.UTIL_CLASSES - ); - cuVolumesMinusDecay[sp0Index][ucAllIndex] = sumUtilizationClassValues( - cuVolumesMinusDecay[sp0Index], UtilizationClass.UTIL_CLASSES - ); - cuVolumesMinusDecayAndWastage[sp0Index][ucAllIndex] = sumUtilizationClassValues( - cuVolumesMinusDecayAndWastage[sp0Index], UtilizationClass.UTIL_CLASSES - ); - - if (basalAreas[sp0Index][ucAllIndex] > 0.0f) { - quadMeanDiameters[sp0Index][ucAllIndex] = BaseAreaTreeDensityDiameter - .quadMeanDiameter(basalAreas[sp0Index][ucAllIndex], treesPerHectare[sp0Index][ucAllIndex]); - } - } - - // Layer - - basalAreas[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues(basalAreas, UtilizationClass.ALL); - treesPerHectare[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - treesPerHectare, UtilizationClass.ALL - ); - wholeStemVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - wholeStemVolumes, UtilizationClass.ALL - ); - closeUtilizationVolumes[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - closeUtilizationVolumes, UtilizationClass.ALL - ); - cuVolumesMinusDecay[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecay, UtilizationClass.ALL - ); - cuVolumesMinusDecayAndWastage[layerIndex][ucAllIndex] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecayAndWastage, UtilizationClass.ALL - ); - - // Calculate the layer's uc All values for quad-mean-diameter and lorey height - - float sumLoreyHeightByBasalAreaSmall = 0.0f; - float sumBasalAreaSmall = 0.0f; - float sumLoreyHeightByBasalAreaAll = 0.0f; - - for (int sp0Index : indices) { - sumLoreyHeightByBasalAreaSmall += loreyHeights[sp0Index][ucSmallIndex] * basalAreas[sp0Index][ucSmallIndex]; - sumBasalAreaSmall += basalAreas[sp0Index][ucSmallIndex]; - sumLoreyHeightByBasalAreaAll += loreyHeights[sp0Index][ucAllIndex] * basalAreas[sp0Index][ucAllIndex]; - } - - if (basalAreas[layerIndex][ucAllIndex] > 0.0f) { - quadMeanDiameters[layerIndex][ucAllIndex] = BaseAreaTreeDensityDiameter - .quadMeanDiameter(basalAreas[layerIndex][ucAllIndex], treesPerHectare[layerIndex][ucAllIndex]); - loreyHeights[layerIndex][ucAllIndex] = sumLoreyHeightByBasalAreaAll / basalAreas[layerIndex][ucAllIndex]; - } - - // Calculate the layer's lorey height uc Small value - - if (sumBasalAreaSmall > 0.0f) { - loreyHeights[layerIndex][ucSmallIndex] = sumLoreyHeightByBasalAreaSmall / sumBasalAreaSmall; - } - - // Finally, set the layer's summable UC values (other than All, which was computed above) to - // the sums of those of each of the species. - - for (UtilizationClass uc : UtilizationClass.ALL_CLASSES) { - basalAreas[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(basalAreas, uc); - treesPerHectare[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(treesPerHectare, uc); - wholeStemVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(wholeStemVolumes, uc); - closeUtilizationVolumes[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( - closeUtilizationVolumes, uc - ); - cuVolumesMinusDecay[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues(cuVolumesMinusDecay, uc); - cuVolumesMinusDecayAndWastage[layerIndex][uc.ordinal()] = sumSpeciesUtilizationClassValues( - cuVolumesMinusDecayAndWastage, uc - ); - } - } - - private float sumUtilizationClassValues(float[] ucValues, List subjects) { - float sum = 0.0f; - - for (UtilizationClass uc : UtilizationClass.values()) { - if (subjects.contains(uc)) { - sum += ucValues[uc.ordinal()]; - } - } - - return sum; - } - - private float sumSpeciesUtilizationClassValues(float[][] ucValues, UtilizationClass uc) { - float sum = 0.0f; - - for (int sp0Index : this.indices) { - sum += ucValues[sp0Index][uc.ordinal()]; - } - - return sum; - } - - /** - * This method copies the Bank contents out to the VdypLayer instance used to create it and returns that. It is a - * relatively expensive operation and should not be called without due consideration. - * - * @return as described - */ - VdypLayer buildLayerFromBank() { - - transferUtilizationsFromBank(0, layer); - - Collection newSpecies = new ArrayList<>(); - for (int i : indices) { - newSpecies.add(transferSpeciesFromBank(i, layer.getSpecies().get(speciesNames[i]))); - } - layer.setSpecies(newSpecies); - - return layer; - } - - private VdypSpecies transferSpeciesFromBank(int index, VdypSpecies species) { - - VdypSpecies newSpecies = VdypSpecies.build(speciesBuilder -> { - speciesBuilder.copy(species); - speciesBuilder.percentGenus(this.percentagesOfForestedLand[index]); - species.getSite().ifPresentOrElse(site -> speciesBuilder.addSite(VdypSite.build(siteBuilder -> { - siteBuilder.copy(site); - siteBuilder.siteGenus(this.speciesNames[index]); - siteBuilder.ageTotal(Utils.optFloat(ageTotals[index])); - siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); - siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); - siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); - siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); - })), () -> { - VdypSite site = VdypSite.build(siteBuilder -> { - siteBuilder.polygonIdentifier(species.getPolygonIdentifier()); - siteBuilder.layerType(species.getLayerType()); - siteBuilder.siteGenus(this.speciesNames[index]); - siteBuilder.ageTotal(Utils.optFloat(this.ageTotals[index])); - siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); - siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); - siteBuilder.siteIndex(Utils.optFloat(this.siteIndices[index])); - siteBuilder.yearsToBreastHeight(Utils.optFloat(this.yearsToBreastHeight[index])); - }); - - speciesBuilder.addSite(site); - }); - }); - - transferUtilizationsFromBank(index, newSpecies); - - return newSpecies; - } - - private void transferUtilizationsFromBank(int index, VdypUtilizationHolder uh) { - - for (UtilizationClass uc : UtilizationClass.values()) { - int ucIndex = uc.ordinal(); - uh.getBaseAreaByUtilization().set(uc, basalAreas[index][ucIndex]); - uh.getCloseUtilizationVolumeByUtilization().set(uc, closeUtilizationVolumes[index][ucIndex]); - uh.getCloseUtilizationVolumeNetOfDecayByUtilization().set(uc, cuVolumesMinusDecay[index][ucIndex]); - uh.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() - .set(uc, cuVolumesMinusDecayAndWastage[index][ucIndex]); - if (ucIndex < 2 /* only uc 0 and 1 have a lorey height */) { - uh.getLoreyHeightByUtilization().set(uc, loreyHeights[index][ucIndex]); - } - uh.getQuadraticMeanDiameterByUtilization().set(uc, quadMeanDiameters[index][ucIndex]); - uh.getTreesPerHectareByUtilization().set(uc, treesPerHectare[index][ucIndex]); - uh.getWholeStemVolumeByUtilization().set(uc, wholeStemVolumes[index][ucIndex]); - } - } - - public Bank copy() { - return new Bank(this); - } - - private String[] copy(String[] a) { - return Arrays.stream(a).toArray(String[]::new); - } - - private int[] copy(int[] a) { - int[] t = new int[a.length]; - - System.arraycopy(a, 0, t, 0, a.length); - - return t; - } - - private float[] copy(float[] a) { - float[] t = new float[a.length]; - - System.arraycopy(a, 0, t, 0, a.length); - - return t; - } - - private float[][] copy(float[][] a) { - return Arrays.stream(a).map(float[]::clone).toArray(float[][]::new); - } - - private Sp64DistributionSet[] copy(Sp64DistributionSet[] sp64Distributions) { - return Arrays.stream(sp64Distributions).map(s -> s == null ? null : s.copy()) - .toArray(Sp64DistributionSet[]::new); - } -} \ No newline at end of file diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index a3425ea5a..2421dc74e 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -66,6 +66,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.si32.site.SiteTool; /** diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java index 60247c49c..9658b589a 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java @@ -18,6 +18,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class LayerProcessingState { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java index 151d71b02..ca2fcaa0d 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java @@ -22,6 +22,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParseException; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class Grow10StoreSpeciesDetails { diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java index 8f3e2b11b..e02234555 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java @@ -118,174 +118,4 @@ public static void setGrowthTargetYear(ForwardProcessingState fps, int targetYea } } - public static VdypLayer normalizeLayer(VdypLayer layer) { - - // Set uc All to the sum of the UC values, UC 7.5 and above only, for the summable - // values, and calculate quad-mean-diameter from these values. - - UtilizationClass ucAll = UtilizationClass.ALL; - UtilizationClass ucSmall = UtilizationClass.SMALL; - - for (var species : layer.getSpecies().values()) { - - species.getBaseAreaByUtilization().set( - ucAll, sumUtilizationClassValues(species.getBaseAreaByUtilization(), UtilizationClass.UTIL_CLASSES) - ); - species.getTreesPerHectareByUtilization().set( - ucAll, - sumUtilizationClassValues(species.getTreesPerHectareByUtilization(), UtilizationClass.UTIL_CLASSES) - ); - species.getWholeStemVolumeByUtilization().set( - ucAll, - sumUtilizationClassValues(species.getWholeStemVolumeByUtilization(), UtilizationClass.UTIL_CLASSES) - ); - species.getCloseUtilizationVolumeByUtilization().set( - ucAll, - sumUtilizationClassValues( - species.getCloseUtilizationVolumeByUtilization(), UtilizationClass.UTIL_CLASSES - ) - ); - species.getCloseUtilizationVolumeNetOfDecayByUtilization().set( - ucAll, - sumUtilizationClassValues( - species.getCloseUtilizationVolumeNetOfDecayByUtilization(), UtilizationClass.UTIL_CLASSES - ) - ); - species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().set( - ucAll, - sumUtilizationClassValues( - species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - UtilizationClass.UTIL_CLASSES - ) - ); - - if (species.getBaseAreaByUtilization().get(ucAll) > 0.0f) { - species.getQuadraticMeanDiameterByUtilization().set( - ucAll, - BaseAreaTreeDensityDiameter.quadMeanDiameter( - species.getBaseAreaByUtilization().get(ucAll), - species.getTreesPerHectareByUtilization().get(ucAll) - ) - ); - } - } - - // Set the layer's uc All values (for summable types) to the sum of those of the - // individual species. - - layer.getBaseAreaByUtilization() - .set(ucAll, sumSpeciesUtilizationClassValues(layer, "BaseArea", UtilizationClass.ALL)); - layer.getTreesPerHectareByUtilization() - .set(ucAll, sumSpeciesUtilizationClassValues(layer, "TreesPerHectare", UtilizationClass.ALL)); - layer.getWholeStemVolumeByUtilization() - .set(ucAll, sumSpeciesUtilizationClassValues(layer, "WholeStemVolume", UtilizationClass.ALL)); - layer.getCloseUtilizationVolumeByUtilization() - .set(ucAll, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolume", UtilizationClass.ALL)); - layer.getCloseUtilizationVolumeNetOfDecayByUtilization().set( - ucAll, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecay", UtilizationClass.ALL) - ); - layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().set( - ucAll, - sumSpeciesUtilizationClassValues( - layer, "CloseUtilizationVolumeNetOfDecayAndWaste", UtilizationClass.ALL - ) - ); - - // Calculate the layer's uc All values for quad-mean-diameter and lorey height - - float sumLoreyHeightByBasalAreaSmall = 0.0f; - float sumBasalAreaSmall = 0.0f; - float sumLoreyHeightByBasalAreaAll = 0.0f; - - for (var species : layer.getSpecies().values()) { - sumLoreyHeightByBasalAreaSmall += species.getLoreyHeightByUtilization().get(ucSmall) - * species.getBaseAreaByUtilization().get(ucSmall); - sumBasalAreaSmall += species.getBaseAreaByUtilization().get(ucSmall); - sumLoreyHeightByBasalAreaAll += species.getLoreyHeightByUtilization().get(ucAll) - * species.getBaseAreaByUtilization().get(ucAll); - } - - if (layer.getBaseAreaByUtilization().get(ucAll) > 0.0f) { - layer.getQuadraticMeanDiameterByUtilization().set( - ucAll, - BaseAreaTreeDensityDiameter.quadMeanDiameter( - layer.getBaseAreaByUtilization().get(ucAll), - layer.getTreesPerHectareByUtilization().get(ucAll) - ) - ); - layer.getLoreyHeightByUtilization() - .set(ucAll, sumLoreyHeightByBasalAreaAll / layer.getBaseAreaByUtilization().get(ucAll)); - } - - // Calculate the layer's lorey height uc Small value - - if (sumBasalAreaSmall > 0.0f) { - layer.getLoreyHeightByUtilization().set(ucSmall, sumLoreyHeightByBasalAreaSmall / sumBasalAreaSmall); - } - - // Finally, set the layer's summable UC values (other than All, which was computed above) to - // the sums of those of each of the species. - - for (UtilizationClass uc : UtilizationClass.ALL_CLASSES) { - layer.getBaseAreaByUtilization().set(uc, sumSpeciesUtilizationClassValues(layer, "BaseArea", uc)); - layer.getTreesPerHectareByUtilization() - .set(uc, sumSpeciesUtilizationClassValues(layer, "TreesPerHectare", uc)); - layer.getWholeStemVolumeByUtilization() - .set(uc, sumSpeciesUtilizationClassValues(layer, "WholeStemVolume", uc)); - layer.getCloseUtilizationVolumeByUtilization() - .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolume", uc)); - layer.getCloseUtilizationVolumeNetOfDecayByUtilization() - .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecay", uc)); - layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization() - .set(uc, sumSpeciesUtilizationClassValues(layer, "CloseUtilizationVolumeNetOfDecayAndWaste", uc)); - } - - return layer; - } - - private static float sumUtilizationClassValues(UtilizationVector ucValues, List subjects) { - float sum = 0.0f; - - for (UtilizationClass uc : UtilizationClass.values()) { - if (subjects.contains(uc)) { - sum += ucValues.get(uc); - } - } - - return sum; - } - - private static float sumSpeciesUtilizationClassValues(VdypLayer layer, String uvName, UtilizationClass uc) { - float sum = 0.0f; - - for (VdypSpecies species : layer.getSpecies().values()) { - switch (uvName) { - case "CloseUtilizationVolumeNetOfDecayWasteAndBreakage": - sum += species.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization().get(uc); - break; - case "CloseUtilizationVolumeNetOfDecayAndWaste": - sum += species.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().get(uc); - break; - case "CloseUtilizationVolumeNetOfDecay": - sum += species.getCloseUtilizationVolumeNetOfDecayByUtilization().get(uc); - break; - case "CloseUtilizationVolume": - sum += species.getCloseUtilizationVolumeByUtilization().get(uc); - break; - case "WholeStemVolume": - sum += species.getWholeStemVolumeByUtilization().get(uc); - break; - case "TreesPerHectare": - sum += species.getTreesPerHectareByUtilization().get(uc); - break; - case "BaseArea": - sum += species.getBaseAreaByUtilization().get(uc); - break; - default: - throw new IllegalStateException(uvName + " is not a known utilization vector name"); - } - } - - return sum; - } } From 51774cd6ee601f7d262796cdbec4f83cf15c9d2a Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 18 Oct 2024 14:33:17 -0700 Subject: [PATCH 08/45] Improve Bank unit tests --- .../bc/gov/nrs/vdyp/processing_state/BankTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index fa0b44267..c525be524 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -62,10 +62,23 @@ void before() throws IOException, ResourceParseException { lb.addSpecies(sb -> { sb.genus("H", controlMap); sb.baseArea(50f); + sb.addSite(ib->{ + ib.ageTotal(100); + ib.yearsToBreastHeight(5); + ib.siteIndex(0.6f); + ib.height(20f); + ib.siteCurveNumber(10); + }); }); lb.addSpecies(sb -> { sb.genus("S", controlMap); sb.baseArea(99.9f); + sb.addSite(ib->{ + ib.ageTotal(100); + ib.yearsToBreastHeight(5); + ib.siteIndex(0.6f); + ib.height(20f); + }); }); }); }); From 3ed2b79d242a0e686908c4871f051d366679fdb0 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 18 Oct 2024 15:22:27 -0700 Subject: [PATCH 09/45] Fix formatting --- .../java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index c525be524..e70dbc0f1 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -62,7 +62,7 @@ void before() throws IOException, ResourceParseException { lb.addSpecies(sb -> { sb.genus("H", controlMap); sb.baseArea(50f); - sb.addSite(ib->{ + sb.addSite(ib -> { ib.ageTotal(100); ib.yearsToBreastHeight(5); ib.siteIndex(0.6f); @@ -73,11 +73,11 @@ void before() throws IOException, ResourceParseException { lb.addSpecies(sb -> { sb.genus("S", controlMap); sb.baseArea(99.9f); - sb.addSite(ib->{ + sb.addSite(ib -> { ib.ageTotal(100); ib.yearsToBreastHeight(5); ib.siteIndex(0.6f); - ib.height(20f); + ib.height(20f); }); }); }); From c5efff376a84974b1521ed0e8d56a9f9392d4ed8 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 18 Oct 2024 15:43:34 -0700 Subject: [PATCH 10/45] Move VDYP Model object parsers from forward to common --- .../model}/VdypPolygonDescriptionParser.java | 2 +- .../io/parse/model}/VdypPolygonParser.java | 2 +- .../io/parse/model}/VdypSpeciesParser.java | 14 ++-- .../parse/model}/VdypUtilizationParser.java | 20 +++--- .../nrs/vdyp/io/parse/value/ValueParser.java | 56 ++++++++++++++++ .../nrs/vdyp/processing_state/BankTest.java | 24 +++++++ .../vdyp/forward/ForwardControlParser.java | 8 +-- .../parsers/VdypForwardDefaultingParser.java | 64 ------------------- .../AbstractForwardProcessingEngineTest.java | 6 +- .../forward/ForwardPolygonParserTest.java | 2 +- ...wardProcessorCheckpointGenerationTest.java | 6 +- .../forward/ForwardProcessorEndToEndTest.java | 6 +- .../forward/ForwardSpeciesParserTest.java | 2 +- .../forward/ForwardUtilizationParserTest.java | 2 +- .../test/Vdyp7OutputControlParser.java | 8 +-- 15 files changed, 119 insertions(+), 103 deletions(-) rename lib/{vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers => vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model}/VdypPolygonDescriptionParser.java (98%) rename lib/{vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers => vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model}/VdypPolygonParser.java (99%) rename lib/{vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers => vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model}/VdypSpeciesParser.java (94%) rename lib/{vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers => vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model}/VdypUtilizationParser.java (88%) delete mode 100644 lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypForwardDefaultingParser.java diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonDescriptionParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java similarity index 98% rename from lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonDescriptionParser.java rename to lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java index 4fa9c991f..be1d07dfb 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonDescriptionParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.forward.parsers; +package ca.bc.gov.nrs.vdyp.io.parse.model; import java.io.IOException; import java.util.Map; diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParser.java similarity index 99% rename from lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonParser.java rename to lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParser.java index b949f320a..9869af14e 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypPolygonParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParser.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.forward.parsers; +package ca.bc.gov.nrs.vdyp.io.parse.model; import java.io.IOException; import java.text.MessageFormat; diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypSpeciesParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java similarity index 94% rename from lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypSpeciesParser.java rename to lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java index 968de8931..b61b100fa 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypSpeciesParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.forward.parsers; +package ca.bc.gov.nrs.vdyp.io.parse.model; import java.io.IOException; import java.util.ArrayList; @@ -86,13 +86,13 @@ public ControlKey getControlKey() { .value(5, PERCENT_SPECIES_2, ControlledValueParser.optional(ValueParser.PERCENTAGE)) .value(3, SPECIES_3, ControlledValueParser.optional(ControlledValueParser.SPECIES)) .value(5, PERCENT_SPECIES_3, ControlledValueParser.optional(ValueParser.PERCENTAGE)) - .value(6, SITE_INDEX, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(6, DOMINANT_HEIGHT, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(6, TOTAL_AGE, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(6, AGE_AT_BREAST_HEIGHT, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(6, YEARS_TO_BREAST_HEIGHT, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) + .value(6, SITE_INDEX, ValueParser.FLOAT_WITH_DEFAULT) + .value(6, DOMINANT_HEIGHT, ValueParser.FLOAT_WITH_DEFAULT) + .value(6, TOTAL_AGE, ValueParser.FLOAT_WITH_DEFAULT) + .value(6, AGE_AT_BREAST_HEIGHT, ValueParser.FLOAT_WITH_DEFAULT) + .value(6, YEARS_TO_BREAST_HEIGHT, ValueParser.FLOAT_WITH_DEFAULT) .value(2, IS_PRIMARY_SPECIES, ControlledValueParser.optional(ValueParser.LOGICAL_0_1)) - .value(3, SITE_CURVE_NUMBER, VdypForwardDefaultingParser.INTEGER_WITH_DEFAULT); + .value(3, SITE_CURVE_NUMBER, ValueParser.INTEGER_WITH_DEFAULT); var is = fileResolver.resolveForInput(fileName); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypUtilizationParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java similarity index 88% rename from lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypUtilizationParser.java rename to lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java index a8b10f5a6..d9857215c 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypUtilizationParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.forward.parsers; +package ca.bc.gov.nrs.vdyp.io.parse.model; import java.io.IOException; import java.util.Collection; @@ -66,15 +66,15 @@ public ControlKey getControlKey() { .space(1) // .value(2, GENUS, ControlledValueParser.optional(ValueParser.GENUS)) .value(3, UTILIZATION_CLASS_INDEX, ControlledValueParser.UTILIZATION_CLASS) - .value(9, BASAL_AREA, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, LIVE_TREES_PER_HECTARE, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, LOREY_HEIGHT, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, WHOLE_STEM_VOLUME, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, CLOSE_UTIL_VOLUME, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, CU_VOLUME_LESS_DECAY, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, CU_VOLUME_LESS_DECAY_WASTAGE, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(9, CU_VOLUME_LESS_DECAY_WASTAGE_BREAKAGE, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT) - .value(6, QUADRATIC_MEAN_DIAMETER_BREAST_HEIGHT, VdypForwardDefaultingParser.FLOAT_WITH_DEFAULT); + .value(9, BASAL_AREA, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, LIVE_TREES_PER_HECTARE, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, LOREY_HEIGHT, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, WHOLE_STEM_VOLUME, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, CLOSE_UTIL_VOLUME, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, CU_VOLUME_LESS_DECAY, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, CU_VOLUME_LESS_DECAY_WASTAGE, ValueParser.FLOAT_WITH_DEFAULT) + .value(9, CU_VOLUME_LESS_DECAY_WASTAGE_BREAKAGE, ValueParser.FLOAT_WITH_DEFAULT) + .value(6, QUADRATIC_MEAN_DIAMETER_BREAST_HEIGHT, ValueParser.FLOAT_WITH_DEFAULT); var is = fileResolver.resolveForInput(fileName); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java index 1277dd5d5..87b59fd6f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java @@ -11,9 +11,12 @@ import java.util.function.Function; import java.util.function.Predicate; +import org.apache.commons.lang3.StringUtils; + import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.Region; +import ca.bc.gov.nrs.vdyp.model.VdypEntity; /** * Parses a string to a value @@ -42,6 +45,42 @@ default T parse(String string, Map control) throws ValueParseExc return this.parse(string); } + /** + * Validate that a parsed value is greater than 0 and less than (or, if includeMax is true, equal to) max. + * Additionally, if the value is -9.0, it is considered "not present" and Float.NaN is returns. + * + * @param parser underlying parser + * @param max the upper bound + * @param includeMax is the upper bound inclusive + * @param name Name for the value to use in the parse error if it is out of the range. + */ + static > ValueParser rangeSilentWithDefaulting( + ValueParser parser, T min, boolean includeMin, T max, boolean includeMax, T missingIndicator, + T defaultValue, String name + ) { + return ValueParser.validate(s -> { + var result = defaultValue; + if (!StringUtils.isEmpty(s)) { + result = parser.parse(s); + if (missingIndicator.equals(result)) { + result = defaultValue; + } + } + return result; + }, result -> { + if (!defaultValue.equals(result) && (result.compareTo(max) > (includeMax ? 0 : -1) + || result.compareTo(min) < (includeMin ? 0 : 1))) { + return Optional.of( + String.format( + "{} must be between {} ({}) and {} ({})", name, min, + includeMin ? "inclusive" : "exclusive", max, includeMax ? "inclusive" : "exclusive" + ) + ); + } + return Optional.empty(); + }); + } + public static interface JavaNumberParser { U parse(String s) throws NumberFormatException; } @@ -476,4 +515,21 @@ public static ValueParser callback(ValueParser delegate, Consumer c }; } + /** + * Parser for non-negative single precision floats with default -9.0. -9.0 results in an + * VdypEntity.MISSING_FLOAT_VALUE being returned. All other negative values, and those greater than Float.MAX_VALUE, + * result in an error. + */ + ValueParser FLOAT_WITH_DEFAULT = rangeSilentWithDefaulting( + FLOAT, 0.0f, true, Float.MAX_VALUE, true, -9.0f, VdypEntity.MISSING_FLOAT_VALUE, "non-negative float" + ); + + /** + * Parser for non-negative integers with default -9. -9 results in VdypEntity.MISSING_INTEGER_VALUE being returned. + * All other negative values, and those > Float.MAX_VALUE, result in an error. + */ + ValueParser INTEGER_WITH_DEFAULT = rangeSilentWithDefaulting( + INTEGER, 0, true, Integer.MAX_VALUE, true, -9, VdypEntity.MISSING_INTEGER_VALUE, "non-negative integer" + ); + } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index e70dbc0f1..89aac59c1 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -8,11 +8,15 @@ import java.util.List; import java.util.Map; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; @@ -79,8 +83,27 @@ void before() throws IOException, ResourceParseException { ib.siteIndex(0.6f); ib.height(20f); }); + + sb.quadMeanDiameter(25); + sb.baseArea(26); + sb.treesPerHectare(BaseAreaTreeDensityDiameter.treesPerHectare(26, 25)); + sb.loreyHeight(227); + sb.closeUtilizationVolumeByUtilization(42); + sb.closeUtilizationVolumeNetOfDecayByUtilization(41); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(40); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(39); }); + + lb.quadraticMeanDiameterByUtilization(21); + lb.baseAreaByUtilization(22); + lb.treesPerHectareByUtilization(BaseAreaTreeDensityDiameter.treesPerHectare(22, 21)); + lb.loreyHeightByUtilization(24); + lb.closeUtilizationVolumeByUtilization(42); + lb.closeUtilizationVolumeNetOfDecayByUtilization(41); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(40); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(39); }); + }); } @@ -290,4 +313,5 @@ private void verifyBankSpeciesMatchesSpecies(Bank bank, int index, VdypSpecies s assertThat(bank.siteCurveNumbers[index], is(VdypEntity.MISSING_INTEGER_VALUE)); }); } + } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParser.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParser.java index e38df0d84..f11708055 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParser.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParser.java @@ -17,10 +17,6 @@ import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.forward.parsers.ForwardControlVariableParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonDescriptionParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BasalAreaGrowthEmpiricalParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.BasalAreaGrowthFiatParser; @@ -71,6 +67,10 @@ import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; import ca.bc.gov.nrs.vdyp.io.parse.control.ControlMapValueReplacer; import ca.bc.gov.nrs.vdyp.io.parse.control.ResourceControlMapModifier; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonDescriptionParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParser; /** diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypForwardDefaultingParser.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypForwardDefaultingParser.java deleted file mode 100644 index 3742d5417..000000000 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/parsers/VdypForwardDefaultingParser.java +++ /dev/null @@ -1,64 +0,0 @@ -package ca.bc.gov.nrs.vdyp.forward.parsers; - -import java.util.Optional; - -import org.apache.commons.lang3.StringUtils; - -import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParser; -import ca.bc.gov.nrs.vdyp.model.VdypEntity; - -public interface VdypForwardDefaultingParser extends ValueParser { - - /** - * Parser for non-negative single precision floats with default -9.0. -9.0 results in an - * VdypEntity.MISSING_FLOAT_VALUE being returned. All other negative values, and those greater than Float.MAX_VALUE, - * result in an error. - */ - public static final ValueParser FLOAT_WITH_DEFAULT = rangeSilentWithDefaulting( - FLOAT, 0.0f, true, Float.MAX_VALUE, true, -9.0f, VdypEntity.MISSING_FLOAT_VALUE, "non-negative float" - ); - - /** - * Parser for non-negative integers with default -9. -9 results in VdypEntity.MISSING_INTEGER_VALUE being returned. - * All other negative values, and those > Float.MAX_VALUE, result in an error. - */ - public static final ValueParser INTEGER_WITH_DEFAULT = rangeSilentWithDefaulting( - INTEGER, 0, true, Integer.MAX_VALUE, true, -9, VdypEntity.MISSING_INTEGER_VALUE, "non-negative integer" - ); - - /** - * Validate that a parsed value is greater than 0 and less than (or, if includeMax is true, equal to) max. - * Additionally, if the value is -9.0, it is considered "not present" and Float.NaN is returns. - * - * @param parser underlying parser - * @param max the upper bound - * @param includeMax is the upper bound inclusive - * @param name Name for the value to use in the parse error if it is out of the range. - */ - public static > ValueParser rangeSilentWithDefaulting( - ValueParser parser, T min, boolean includeMin, T max, boolean includeMax, T missingIndicator, - T defaultValue, String name - ) { - return ValueParser.validate(s -> { - var result = defaultValue; - if (!StringUtils.isEmpty(s)) { - result = parser.parse(s); - if (missingIndicator.equals(result)) { - result = defaultValue; - } - } - return result; - }, result -> { - if (!defaultValue.equals(result) && (result.compareTo(max) > (includeMax ? 0 : -1) - || result.compareTo(min) < (includeMin ? 0 : 1))) { - return Optional.of( - String.format( - "{} must be between {} ({}) and {} ({})", name, min, - includeMin ? "inclusive" : "exclusive", max, includeMax ? "inclusive" : "exclusive" - ) - ); - } - return Optional.empty(); - }); - } -} diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/AbstractForwardProcessingEngineTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/AbstractForwardProcessingEngineTest.java index a3884dfff..5a776e8bc 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/AbstractForwardProcessingEngineTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/AbstractForwardProcessingEngineTest.java @@ -9,11 +9,11 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardPolygonParserTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardPolygonParserTest.java index 44008575d..1804e9f1a 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardPolygonParserTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardPolygonParserTest.java @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.model.PolygonMode; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java index 6df64445a..d41d83fd0 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorCheckpointGenerationTest.java @@ -18,14 +18,14 @@ import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.GenusDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.test.TestUtils; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java index 3e16c1843..84fff4fa4 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessorEndToEndTest.java @@ -22,9 +22,6 @@ import ca.bc.gov.nrs.vdyp.application.Pass; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.FileSystemFileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; @@ -33,6 +30,9 @@ import ca.bc.gov.nrs.vdyp.io.parse.coe.GenusDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.VolumeEquationGroupParser; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypLayer; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardSpeciesParserTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardSpeciesParserTest.java index 143809bcf..2dfc2a487 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardSpeciesParserTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardSpeciesParserTest.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.model.LayerType; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardUtilizationParserTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardUtilizationParserTest.java index f9ddebc43..363b42656 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardUtilizationParserTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardUtilizationParserTest.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.model.LayerType; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/Vdyp7OutputControlParser.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/Vdyp7OutputControlParser.java index b3d57f6c4..0e06f2a58 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/Vdyp7OutputControlParser.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/Vdyp7OutputControlParser.java @@ -15,10 +15,6 @@ import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.forward.parsers.ForwardControlVariableParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonDescriptionParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypPolygonParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypSpeciesParser; -import ca.bc.gov.nrs.vdyp.forward.parsers.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.BreakageEquationGroupParser; @@ -29,6 +25,10 @@ import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; import ca.bc.gov.nrs.vdyp.io.parse.control.ControlMapValueReplacer; import ca.bc.gov.nrs.vdyp.io.parse.control.ResourceControlMapModifier; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonDescriptionParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypPolygonParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypUtilizationParser; import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParser; /** From a718fb3281e2aebb018d7d969484545001d1231f Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 21 Oct 2024 12:08:10 -0700 Subject: [PATCH 11/45] Unit test for back processing engine --- .../vdyp/back/BackProcessingEngineTest.java | 386 ++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java new file mode 100644 index 000000000..05be1a747 --- /dev/null +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -0,0 +1,386 @@ +package ca.bc.gov.nrs.vdyp.back; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.back.processing_state.BackProcessingState; +import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; +import ca.bc.gov.nrs.vdyp.math.FloatMath; +import ca.bc.gov.nrs.vdyp.model.BecLookup; +import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.test.TestUtils; + +class BackProcessingEngineTest { + + BackProcessingEngine engine; + + Map controlMap; + + BecLookup becLookup; + + @BeforeEach + void setup() { + engine = new BackProcessingEngine(); + + controlMap = TestUtils.loadControlMap(); // TODO switch to appropriate control file for Back. + + becLookup = Utils.parsedControl(controlMap, ControlKey.BEC_DEF, BecLookup.class).get(); + + } + + @Nested + class Prepare { + + public BackProcessingState primaryOnlyWithSingleSpecies() throws ProcessingException { + + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.percentAvailable(80f); + pb.biogeoclimaticZone(becLookup.get("IDF").get()); + pb.forestInventoryZone(""); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + + final float ba = 5.0969491f; + final float hl = 22.9584007f; + final float dq = 31.5006275f; + final float tph = BaseAreaTreeDensityDiameter.treesPerHectare(ba, dq); + lb.addSpecies(sb -> { + sb.genus("F", controlMap); + sb.baseArea(ba); + sb.loreyHeight(hl); + sb.quadMeanDiameter(dq); + sb.treesPerHectare(tph); + }); + + lb.baseAreaByUtilization(ba); + lb.loreyHeightByUtilization(hl); + lb.quadraticMeanDiameterByUtilization(dq); + lb.treesPerHectareByUtilization(tph); + }); + }); + + var state = new BackProcessingState(controlMap); + + state.setPolygon(polygon); + + @SuppressWarnings("unchecked") + MatrixMap3[] cvVolume = new MatrixMap3[] { null, + new MatrixMap3Impl( + List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), + List.of(LayerType.values()), + (uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + + @SuppressWarnings("unchecked") + MatrixMap2[] cvBa = new MatrixMap2[] { null, + new MatrixMap2Impl( + List.of(UtilizationClass.values()), List.of(LayerType.values()), + (uc, lt) -> 13f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + + @SuppressWarnings("unchecked") + MatrixMap2[] cvDq = new MatrixMap2[] { null, + new MatrixMap2Impl( + List.of(UtilizationClass.values()), List.of(LayerType.values()), + (uc, lt) -> 17f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + @SuppressWarnings("unchecked") + Map[] cvSm = new EnumMap[] { null, + new EnumMap(UtilizationClassVariable.class) }; + + for (var uc : UtilizationClassVariable.values()) { + cvSm[1].put(uc, uc.ordinal() * 7f); + } + + state.getPrimaryLayerProcessingState().setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm); + + return state; + }; + + public BackProcessingState primaryAndVeteran() throws ProcessingException { + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.percentAvailable(80f); + pb.biogeoclimaticZone(becLookup.get("IDF").get()); + pb.forestInventoryZone(""); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + }); + pb.addLayer(lb -> { + lb.layerType(LayerType.VETERAN); + lb.baseAreaByUtilization(20f); + + lb.addSpecies(sb -> { + sb.genus("F", controlMap); + sb.baseArea(20f); + }); + }); + }); + + var state = new BackProcessingState(controlMap); + + state.setPolygon(polygon); + + return state; + }; + + @Nested + class SetBav { + + @Test + void testSetBavIfVeteranPresent() throws ProcessingException { + + var state = primaryAndVeteran(); + + engine.prepare(state); + + var result = state.getBaseAreaVeteran(); + + assertThat(result, present(is(20f))); + } + + @Test + void testSetBavIfVeteranPresentAndTotalIsWrong() throws ProcessingException { + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.percentAvailable(80f); + pb.biogeoclimaticZone(becLookup.get("IDF").get()); + pb.forestInventoryZone(""); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + }); + pb.addLayer(lb -> { + lb.layerType(LayerType.VETERAN); + lb.baseAreaByUtilization(42f); // Not the sum of the base areas of the species + lb.addSpecies(sb -> { + sb.genus("F", controlMap); + sb.baseArea(20f); + }); + }); + }); + + var state = new BackProcessingState(controlMap); + + state.setPolygon(polygon); + engine.prepare(state); + + var result = state.getBaseAreaVeteran(); + + assertThat(result, present(is(20f))); // Should be calculated from the species + } + + @Test + void testSetBavIfNoVeteranPresent() throws ProcessingException { + + var state = primaryOnlyWithSingleSpecies(); + + engine.prepare(state); + + var result = state.getBaseAreaVeteran(); + + assertThat(result, notPresent()); + } + } + + @Nested + class SetCv { + + @Test + void testSingleSpecies() throws ProcessingException { + + var state = primaryOnlyWithSingleSpecies(); + + engine.prepare(state); + + final int specIndex = 1; // Only one species + + assertThat(state, backCV("getCVBasalArea", specIndex, UtilizationClass.ALL, is(16f))); + assertThat(state, backCV("getCVBasalArea", specIndex, UtilizationClass.U75TO125, is(19f))); + assertThat(state, backCV("getCVBasalArea", specIndex, UtilizationClass.U125TO175, is(22f))); + assertThat(state, backCV("getCVBasalArea", specIndex, UtilizationClass.U175TO225, is(25f))); + assertThat(state, backCV("getCVBasalArea", specIndex, UtilizationClass.OVER225, is(28f))); + + assertThat(state, backCV("getCVQuadraticMeanDiameter", specIndex, UtilizationClass.ALL, is(20f))); + assertThat(state, backCV("getCVQuadraticMeanDiameter", specIndex, UtilizationClass.U75TO125, is(23f))); + assertThat(state, backCV("getCVQuadraticMeanDiameter", specIndex, UtilizationClass.U125TO175, is(26f))); + assertThat(state, backCV("getCVQuadraticMeanDiameter", specIndex, UtilizationClass.U175TO225, is(29f))); + assertThat(state, backCV("getCVQuadraticMeanDiameter", specIndex, UtilizationClass.OVER225, is(32f))); + + } + + } + + @Nested + class SetLimits { + + @Test + void testSingleSpecies() throws ProcessingException { + + var state = primaryOnlyWithSingleSpecies(); + + engine.prepare(state); + + var result = state.getLimits(1); + + assertThat(result, componentSizeLimits(39.9f, 75.8f, 0.792f, 2.155f, 0.0001f)); + + } + } + + } + + static Matcher componentSizeLimits( + float loreyHeightMaximum, float quadMeanDiameterMaximum, float minQuadMeanDiameterLoreyHeightRatio, + float maxQuadMeanDiameterLoreyHeightRatio, float epsilon + ) { + return new TypeSafeDiagnosingMatcher(ComponentSizeLimits.class) { + + @Override + public void describeTo(Description description) { + description.appendText("ComponentSizeLimits with"); + description.appendText(" "); + description.appendText("loreyHeightMaximum = ").appendValue(loreyHeightMaximum); + description.appendText(", "); + description.appendText("quadMeanDiameterMaximum = ").appendValue(quadMeanDiameterMaximum); + description.appendText(", "); + description.appendText("minQuadMeanDiameterLoreyHeightRatio = ") + .appendValue(minQuadMeanDiameterLoreyHeightRatio); + description.appendText(", and "); + description.appendText("maxQuadMeanDiameterLoreyHeightRatio = ") + .appendValue(maxQuadMeanDiameterLoreyHeightRatio); + + description.appendText(" (all to within ±" + epsilon + ")"); + } + + static boolean matchFloatsSafely(float o1, float o2, float epsilon) { + if (Float.isNaN(o1) && Float.isNaN(o2)) + return true; + if (Float.isNaN(o1) || Float.isNaN(o2)) + return false; + return FloatMath.abs(o1 - o2) <= epsilon; + } + + @Override + protected boolean matchesSafely(ComponentSizeLimits item, Description mismatchDescription) { + boolean match = true; + if (!matchFloatsSafely(item.loreyHeightMaximum(), loreyHeightMaximum, epsilon)) { + match = false; + mismatchDescription.appendText("loreyHeightMaximum was ").appendValue(item.loreyHeightMaximum()); + } + if (!matchFloatsSafely(item.quadMeanDiameterMaximum(), quadMeanDiameterMaximum, epsilon)) { + if (!match) + mismatchDescription.appendText(", "); + match = false; + mismatchDescription.appendText("quadMeanDiameterMaximum was ") + .appendValue(item.quadMeanDiameterMaximum()); + } + if (!matchFloatsSafely( + item.minQuadMeanDiameterLoreyHeightRatio(), minQuadMeanDiameterLoreyHeightRatio, epsilon + )) { + if (!match) + mismatchDescription.appendText(", "); + match = false; + mismatchDescription.appendText("minQuadMeanDiameterLoreyHeightRatio was ") + .appendValue(item.minQuadMeanDiameterLoreyHeightRatio()); + } + if (!matchFloatsSafely( + item.maxQuadMeanDiameterLoreyHeightRatio(), maxQuadMeanDiameterLoreyHeightRatio, epsilon + )) { + if (!match) + mismatchDescription.appendText(", "); + match = false; + mismatchDescription.appendText("maxQuadMeanDiameterLoreyHeightRatio was ") + .appendValue(item.maxQuadMeanDiameterLoreyHeightRatio()); + } + return match; + } + + }; + } + + static Matcher backCV(String name, Object p1, Object p2, Matcher expected) { + return compatibilityVariable(name, expected, BackProcessingState.class, p1, p2); + } + + static Matcher backCV(String name, Object p1, Matcher expected) { + return compatibilityVariable(name, expected, BackProcessingState.class, p1); + } + + static Matcher + compatibilityVariable(String name, Matcher expected, Class klazz, Object... params) { + return new TypeSafeDiagnosingMatcher<>(klazz) { + + @Override + public void describeTo(Description description) { + description.appendText(name).appendValueList("(", ", ", ") ", params); + description.appendDescriptionOf(expected); + } + + @Override + protected boolean matchesSafely(T item, Description mismatchDescription) { + Method method; + try { + method = klazz.getMethod( + name, + (Class[]) Arrays.stream(params) + .map(o -> o instanceof Integer ? Integer.TYPE : o.getClass()).toArray(Class[]::new) + ); + } catch (NoSuchMethodException e) { + mismatchDescription.appendText("Method " + name + " does not exist"); + return false; + } + + float result; + try { + result = (float) method.invoke(item, params); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + mismatchDescription.appendText(e.getMessage()); + return false; + } + + if (expected.matches(result)) { + return true; + } + + expected.describeMismatch(result, mismatchDescription); + return false; + } + + }; + } +} From abedb9e5700704ab942ecc2ce02cf756e1e3d56b Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 21 Oct 2024 12:52:33 -0700 Subject: [PATCH 12/45] Working on unit tests for parser --- .../model/VdypUtilizationParserTest.java | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java new file mode 100644 index 000000000..eac144f69 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java @@ -0,0 +1,121 @@ +package ca.bc.gov.nrs.vdyp.io.parse.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.test.MockFileResolver; +import ca.bc.gov.nrs.vdyp.test.TestUtils; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; + +class VdypUtilizationParserTest { + + @Test + void testEmpty() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypUtilizationParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + } + } + + @Test + void testOnePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try ( + var is = TestUtils.makeInputStream( + "01002 S000001 00 1970 P 0 -1 0.01513 5.24 7.0166 0.0630 0.0000 0.0000 0.0000 0.0000 6.1", + "01002 S000001 00 1970 P 0 0 44.93259 595.32 30.9724 620.9775 592.2023 580.1681 577.6229 549.0159 31.0", + "01002 S000001 00 1970 P 0 1 0.53100 64.82 -9.0000 2.5979 0.3834 0.3794 0.3788 0.3623 10.2", + "01002 S000001 00 1970 P 0 2 1.27855 71.93 -9.0000 9.1057 6.9245 6.8469 6.8324 6.5384 15.0", + "01002 S000001 00 1970 P 0 3 2.33020 73.60 -9.0000 22.4019 20.1244 19.8884 19.8375 18.9555 20.1", + "01002 S000001 00 1970 P 0 4 40.79285 384.98 -9.0000 586.8720 564.7699 553.0534 550.5741 523.1597 36.7", + "01002 S000001 00 1970 P 3 B -1 0.00000 0.00 8.0272 0.0000 0.0000 0.0000 0.0000 0.0000 6.1", + "01002 S000001 00 1970 P 3 B 0 0.40292 5.16 36.7553 6.2098 5.9592 5.8465 5.8163 5.5177 31.5", + "01002 S000001 00 1970 P 3 B 1 0.00502 0.76 -9.0000 0.0185 0.0009 0.0009 0.0009 0.0009 9.2", + "01002 S000001 00 1970 P 3 B 2 0.01363 0.93 -9.0000 0.0757 0.0498 0.0497 0.0496 0.0475 13.7", + "01002 S000001 00 1970 P 3 B 3 0.02284 0.88 -9.0000 0.1748 0.1521 0.1514 0.1512 0.1445 18.2", + "01002 S000001 00 1970 P 3 B 4 0.36143 2.60 -9.0000 5.9408 5.7564 5.6446 5.6146 5.3249 42.1", + "01002 S000001 00 1970 P 4 C -1 0.01243 4.40 6.4602 0.0507 0.0000 0.0000 0.0000 0.0000 6.0", + "01002 S000001 00 1970 P 4 C 0 5.04597 83.46 22.9584 43.4686 39.4400 36.2634 35.2930 32.9144 27.7", + "01002 S000001 00 1970 P 4 C 1 0.12822 16.12 -9.0000 0.6027 0.1116 0.1094 0.1090 0.1035 10.1", + "01002 S000001 00 1970 P 4 C 2 0.31003 17.87 -9.0000 1.9237 1.4103 1.3710 1.3628 1.2915 14.9", + "01002 S000001 00 1970 P 4 C 3 0.51339 16.55 -9.0000 3.8230 3.3162 3.2127 3.1859 3.0076 19.9", + "01002 S000001 00 1970 P 4 C 4 4.09434 32.92 -9.0000 37.1192 34.6019 31.5703 30.6352 28.5119 39.8", + "01002 S000001 00 1970 P 5 D -1 0.00155 0.47 10.6033 0.0078 0.0000 0.0000 0.0000 0.0000 6.5", + "01002 S000001 00 1970 P 5 D 0 29.30249 287.70 33.7440 459.5233 444.0844 436.5280 435.2818 413.6949 36.0", + "01002 S000001 00 1970 P 5 D 1 0.01412 1.64 -9.0000 0.1091 0.0571 0.0566 0.0565 0.0541 10.5", + "01002 S000001 00 1970 P 5 D 2 0.05128 2.69 -9.0000 0.5602 0.5048 0.5007 0.5005 0.4783 15.6", + "01002 S000001 00 1970 P 5 D 3 0.45736 13.82 -9.0000 6.0129 5.6414 5.5975 5.5948 5.3383 20.5", + "01002 S000001 00 1970 P 5 D 4 28.77972 269.56 -9.0000 452.8412 437.8810 430.3732 429.1300 407.8242 36.9", + "01002 S000001 00 1970 P 8 H -1 0.00000 0.00 7.5464 0.0000 0.0000 0.0000 0.0000 0.0000 -9.0", + "01002 S000001 00 1970 P 8 H 0 5.81006 167.90 22.7704 55.8878 49.8291 49.0742 48.8550 46.6828 21.0", + "01002 S000001 00 1970 P 8 H 1 0.36138 43.57 -9.0000 1.7385 0.1925 0.1913 0.1911 0.1834 10.3", + "01002 S000001 00 1970 P 8 H 2 0.82449 45.99 -9.0000 5.8666 4.4155 4.3846 4.3789 4.2023 15.1", + "01002 S000001 00 1970 P 8 H 3 1.07566 33.93 -9.0000 9.6521 8.5752 8.5019 8.4827 8.1397 20.1", + "01002 S000001 00 1970 P 8 H 4 3.54853 44.42 -9.0000 38.6306 36.6459 35.9963 35.8023 34.1574 31.9", + "01002 S000001 00 1970 P 15 S -1 0.00115 0.36 8.2003 0.0045 0.0000 0.0000 0.0000 0.0000 6.3", + "01002 S000001 00 1970 P 15 S 0 4.37115 51.10 32.0125 55.8879 52.8895 52.4561 52.3768 50.2060 33.0", + "01002 S000001 00 1970 P 15 S 1 0.02225 2.73 -9.0000 0.1291 0.0213 0.0212 0.0212 0.0204 10.2", + "01002 S000001 00 1970 P 15 S 2 0.07911 4.46 -9.0000 0.6795 0.5440 0.5410 0.5406 0.5189 15.0", + "01002 S000001 00 1970 P 15 S 3 0.26095 8.43 -9.0000 2.7391 2.4396 2.4250 2.4229 2.3254 19.9", + "01002 S000001 00 1970 P 15 S 4 4.00883 35.49 -9.0000 52.3402 49.8846 49.4689 49.3920 47.3414 37.9", + "01002 S000001 00 1970 " + ) + ) { + + resolver.addStream("test.dat", is); + + var parser = new VdypUtilizationParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = new ArrayList<>(stream.next()); // Array list makes it indexable so we can easily sample + // entries to test. Checking everything would be excessive. + + assertThat(result, hasSize(6 * UtilizationClass.values().length)); + + result.get(0); + + assertThat( + result.get(1), + allOf( + hasProperty("genus", notPresent()), hasProperty("genusIndex", is(0)), + hasProperty("ucIndex", is(UtilizationClass.ALL)), + hasProperty("basalArea", closeTo(44.93259f)), + hasProperty("liveTreesPerHectare", closeTo(595.32f)), + hasProperty("loreyHeight", closeTo(30.9724f)), + hasProperty("closeUtilizationVolume", closeTo(620.9775f)) + ) + ); + + assertTrue(!stream.hasNext(), "stream is empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + +} From 2c36b843f1f561cdbb695b72f6818d4c7d368353 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 22 Oct 2024 12:27:06 -0700 Subject: [PATCH 13/45] Unit tests for Utilization parser --- .../model/VdypUtilizationParserTest.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java index eac144f69..c9a3194cb 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParserTest.java @@ -97,8 +97,6 @@ void testOnePoly() throws IOException, ResourceParseException { assertThat(result, hasSize(6 * UtilizationClass.values().length)); - result.get(0); - assertThat( result.get(1), allOf( @@ -107,11 +105,29 @@ void testOnePoly() throws IOException, ResourceParseException { hasProperty("basalArea", closeTo(44.93259f)), hasProperty("liveTreesPerHectare", closeTo(595.32f)), hasProperty("loreyHeight", closeTo(30.9724f)), - hasProperty("closeUtilizationVolume", closeTo(620.9775f)) + hasProperty("wholeStemVolume", closeTo(620.9775f)), + hasProperty("closeUtilizationVolume", closeTo(592.2023f)), + hasProperty("cuVolumeMinusDecay", closeTo(580.1681f)), + hasProperty("cuVolumeMinusDecayWastage", closeTo(577.6229f)), + hasProperty("cuVolumeMinusDecayWastageBreakage", closeTo(549.0159f)) ) ); - assertTrue(!stream.hasNext(), "stream is empty"); + assertThat( + result.get(32), + allOf( + hasProperty("genus", present(is("S"))), hasProperty("genusIndex", is(15)), + hasProperty("ucIndex", is(UtilizationClass.U75TO125)), + hasProperty("basalArea", closeTo(0.02225f)), + hasProperty("liveTreesPerHectare", closeTo(2.73f)), + hasProperty("loreyHeight", is(Float.NaN)), hasProperty("wholeStemVolume", closeTo(0.1291f)), + hasProperty("closeUtilizationVolume", closeTo(0.0213f)), + hasProperty("cuVolumeMinusDecay", closeTo(0.0212f)), + hasProperty("cuVolumeMinusDecayWastage", closeTo(0.0212f)), + hasProperty("cuVolumeMinusDecayWastageBreakage", closeTo(0.0204f)) + ) + ); + assertTrue(!stream.hasNext(), "stream is not empty"); assertThrows(NoSuchElementException.class, () -> stream.next()); From f8fad2ab5695a4cde41202d494117d7eecddb761 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 23 Oct 2024 11:22:01 -0700 Subject: [PATCH 14/45] Unit test for parsing species --- .../io/parse/model/VdypSpeciesParserTest.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java new file mode 100644 index 000000000..92ef93b0b --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java @@ -0,0 +1,182 @@ +package ca.bc.gov.nrs.vdyp.io.parse.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.test.MockFileResolver; +import ca.bc.gov.nrs.vdyp.test.TestUtils; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; + +class VdypSpeciesParserTest { + + @Test + void testEmpty() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypSpeciesParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + } + } + + @Test + void testOnePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try ( + var is = TestUtils.makeInputStream( + "01002 S000001 00 1970 P 3 B B 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 4 C C 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 5 D D 100.0 0.0 0.0 0.0 35.00 35.30 55.0 54.0 1.0 1 13", + "01002 S000001 00 1970 P 8 H H 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 15 S S 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 " + ) + ) { + + resolver.addStream("test.dat", is); + + var parser = new VdypSpeciesParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = new ArrayList<>(stream.next()); // Array list makes it indexable so we can easily sample + // entries to test. Checking everything would be excessive. + + assertThat(result, hasSize(5)); + + assertThat( + result.get(0), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000001 00", 1970)), + hasProperty("layerType", is(LayerType.PRIMARY)), hasProperty("genus", is("B")) + ) + ); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + + @Test + void testMultiplePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try ( + var is = TestUtils.makeInputStream( + "01002 S000001 00 1970 P 3 B B 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 4 C C 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 5 D D 100.0 0.0 0.0 0.0 35.00 35.30 55.0 54.0 1.0 1 13", + "01002 S000001 00 1970 P 8 H H 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 P 15 S S 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000001 00 1970 ", + "01002 S000002 00 1970 P 3 B B 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000002 00 1970 P 5 D D 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000002 00 1970 P 8 H H 100.0 0.0 0.0 0.0 28.70 24.30 45.0 39.6 5.4 1 34", + "01002 S000002 00 1970 P 15 S S 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000002 00 1970 V 3 B B 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000002 00 1970 V 8 H H 100.0 0.0 0.0 0.0 16.70 26.20 105.0 97.9 7.1 1 -9", + "01002 S000002 00 1970 V 15 S S 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9", + "01002 S000002 00 1970 " + + ) + ) { + + resolver.addStream("test.dat", is); + + var parser = new VdypSpeciesParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = new ArrayList<>(stream.next()); // Array list makes it indexable so we can easily sample + // entries to test. Checking everything would be excessive. + + assertThat(result, hasSize(5)); + + assertThat( + result.get(0), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000001 00", 1970)), + hasProperty("layerType", is(LayerType.PRIMARY)), hasProperty("genus", is("B")) + ) + ); + assertThat( + result.get(4), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000001 00", 1970)), + hasProperty("layerType", is(LayerType.PRIMARY)), hasProperty("genus", is("S")) + ) + ); + assertTrue(stream.hasNext(), "stream is empty"); + + result = new ArrayList<>(stream.next()); // Array list makes it indexable so we can easily sample + // entries to test. Checking everything would be excessive. + + assertThat(result, hasSize(7)); + + assertThat( + result.get(0), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000002 00", 1970)), + hasProperty("layerType", is(LayerType.PRIMARY)), hasProperty("genus", is("B")) + ) + ); + assertThat( + result.get(3), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000002 00", 1970)), + hasProperty("layerType", is(LayerType.PRIMARY)), hasProperty("genus", is("S")) + ) + ); + assertThat( + result.get(4), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000002 00", 1970)), + hasProperty("layerType", is(LayerType.VETERAN)), hasProperty("genus", is("B")) + ) + ); + assertThat( + result.get(6), + allOf( + hasProperty("polygonIdentifier", VdypMatchers.isPolyId("01002 S000002 00", 1970)), + hasProperty("layerType", is(LayerType.VETERAN)), hasProperty("genus", is("S")) + ) + ); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + +} From 365f8c5ddac3e7e7745e103030547f0693c728ff Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 23 Oct 2024 12:18:10 -0700 Subject: [PATCH 15/45] Unit test for polygon parser --- .../io/parse/model/VdypPolygonParserTest.java | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java new file mode 100644 index 000000000..221d39d9a --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java @@ -0,0 +1,139 @@ +package ca.bc.gov.nrs.vdyp.io.parse.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.test.MockFileResolver; +import ca.bc.gov.nrs.vdyp.test.TestUtils; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; + +class VdypPolygonParserTest { + + @Test + void testEmpty() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + } + } + + @Test + void testOnePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("01002 S000001 00 1970 CWH A 99 37 1 1")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = stream.next(); + + assertThat( + result, + allOf( + hasProperty("polygonIdentifier", isPolyId("01002 S000001 00", 1970)), + hasProperty("biogeoclimaticZone", hasProperty("alias", is("CWH"))) + ) + ); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + + @Test + void testMultiplePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try ( + var is = TestUtils.makeInputStream( + "01002 S000001 00 1970 CWH A 99 37 1 1", + "01002 S000002 00 1970 CWH A 98 15 75 1", + "01002 S000003 00 1970 IDF A 99 15 75 1" + + ) + ) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = stream.next(); + + assertThat( + result, + allOf( + hasProperty("polygonIdentifier", isPolyId("01002 S000001 00", 1970)), + hasProperty("biogeoclimaticZone", hasProperty("alias", is("CWH"))) + ) + ); + + assertTrue(stream.hasNext(), "stream is empty"); + + result = stream.next(); + + assertThat( + result, + allOf( + hasProperty("polygonIdentifier", isPolyId("01002 S000002 00", 1970)), + hasProperty("biogeoclimaticZone", hasProperty("alias", is("CWH"))) + ) + ); + + assertTrue(stream.hasNext(), "stream is empty"); + + result = stream.next(); + + assertThat( + result, + allOf( + hasProperty("polygonIdentifier", isPolyId("01002 S000003 00", 1970)), + hasProperty("biogeoclimaticZone", hasProperty("alias", is("IDF"))) + ) + ); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + +} From d0c9d2231760c8cef1cf17eefc27cab5ca07eb18 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 23 Oct 2024 13:08:25 -0700 Subject: [PATCH 16/45] Refactor to eliminate duplication and branches that were reducing test coverage reporting --- .../io/parse/model/VdypSpeciesParser.java | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java index b61b100fa..31398a1a2 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java @@ -136,25 +136,17 @@ protected ValueOrMarker, EndOfRecord> convert(Map gdList = new ArrayList<>(); - Utils.ifBothPresent( - genusNameText0.filter(t -> genusDefinitionMap.contains(t)), percentGenus0, - (s, p) -> gdList.add(new Sp64Distribution(1, s, p)) - ); - - Utils.ifBothPresent( - genusNameText1.filter(t -> genusDefinitionMap.contains(t)), percentGenus1, - (s, p) -> gdList.add(new Sp64Distribution(2, s, p)) - ); - - Utils.ifBothPresent( - genusNameText2.filter(t -> genusDefinitionMap.contains(t)), percentGenus2, - (s, p) -> gdList.add(new Sp64Distribution(3, s, p)) - ); - - Utils.ifBothPresent( - genusNameText3.filter(t -> genusDefinitionMap.contains(t)), percentGenus3, - (s, p) -> gdList.add(new Sp64Distribution(4, s, p)) - ); + speciesDistribution(genusDefinitionMap, genusNameText0, percentGenus0, 1) + .ifPresent(gdList::add); + + speciesDistribution(genusDefinitionMap, genusNameText1, percentGenus1, 2) + .ifPresent(gdList::add); + + speciesDistribution(genusDefinitionMap, genusNameText2, percentGenus2, 3) + .ifPresent(gdList::add); + + speciesDistribution(genusDefinitionMap, genusNameText3, percentGenus3, 4) + .ifPresent(gdList::add); Sp64DistributionSet speciesDistributionSet = new Sp64DistributionSet(4, gdList); @@ -181,7 +173,7 @@ protected ValueOrMarker, EndOfRecord> convert(Map { siteBuilder.ageTotal(inferredTotalAge); siteBuilder.height(dominantHeight); @@ -196,6 +188,16 @@ protected ValueOrMarker, EndOfRecord> convert(Map speciesDistribution( + GenusDefinitionMap genusDefinitionMap, Optional genusNameText0, + Optional percentGenus0, int index + ) { + return Utils.mapBoth( + genusNameText0.filter(genusDefinitionMap::contains), percentGenus0, + (s, p) -> new Sp64Distribution(index, s, p) + ); + } }; return new GroupingStreamingParser, ValueOrMarker, EndOfRecord>>( From 999597d475739e38f89dd4d0c45c29ad0b6f6f1b Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 23 Oct 2024 13:12:51 -0700 Subject: [PATCH 17/45] Unit test for polygon ID parser --- .../model/VdypPolygonDescriptionParser.java | 4 + .../VdypPolygonDescriptionParserTest.java | 199 ++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java index be1d07dfb..e81f91cb1 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParser.java @@ -33,6 +33,10 @@ public static PolygonIdentifier parse(String description) throws ResourceParseEx Integer year; String name; + if (description.length() != PolygonIdentifier.ID_LENGTH) { + throw new ResourceParseException("Polygon description " + description + " was not 25 characters long"); + } + if (matcher.matches() && matcher.group(2) != null) { year = Integer.parseInt(matcher.group(2)); name = matcher.group(1).trim(); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java new file mode 100644 index 000000000..6c510617d --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java @@ -0,0 +1,199 @@ +package ca.bc.gov.nrs.vdyp.io.parse.model; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.test.MockFileResolver; +import ca.bc.gov.nrs.vdyp.test.TestUtils; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; + +class VdypPolygonDescriptionParserTest { + + @Test + void testEmpty() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + } + } + + @Test + void testOnePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream("01002 S000001 00 1970")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonDescriptionParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = stream.next(); + + assertThat(result, isPolyId("01002 S000001 00", 1970)); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + + @Test + void testMultiplePoly() throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try ( + var is = TestUtils.makeInputStream( + "01002 S000001 00 1970", "01002 S000002 00 1970", "01002 S000003 00 1973" + + ) + ) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonDescriptionParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = stream.next(); + + assertThat(result, isPolyId("01002 S000001 00", 1970)); + + assertTrue(stream.hasNext(), "stream is empty"); + + result = stream.next(); + + assertThat(result, isPolyId("01002 S000002 00", 1970)); + + assertTrue(stream.hasNext(), "stream is empty"); + + result = stream.next(); + + assertThat(result, isPolyId("01002 S000003 00", 1973)); + + assertTrue(!stream.hasNext(), "stream is not empty"); + + assertThrows(NoSuchElementException.class, () -> stream.next()); + + } + } + + @ParameterizedTest + @ValueSource( + strings = { "01002 S000001 00 XXXX", "01002 S000001 00 199X", "01002 S000001 00 999", + "01002 S000001 00 999 ", "01002 S000001 00 19 9", "01002 S000001 00 " } + ) + void testIDsWithoutAValidYear(String id) throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream(id)) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonDescriptionParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var ex = assertThrows(ResourceParseException.class, () -> stream.next()); + + } + + } + + @ParameterizedTest + @ValueSource( + strings = { "01002 S000001 00 2024", // short (24) + "01002 S000001 00 2024" // long (26) + } + ) + void testIdsWithWrongLength(String id) throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream(id)) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonDescriptionParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var ex = assertThrows(ResourceParseException.class, () -> stream.next()); + + } + + } + + @ParameterizedTest + @ValueSource( + strings = { "01002 S000001 00 XXXX", // bad year + "01002 S000001 00 2024", // short + "01002 S000001 00 2024" // long + } + ) + void testCanProgressToNextLineAfterError(String id) throws IOException, ResourceParseException { + var controlMap = TestUtils.loadControlMap(); + var resolver = new MockFileResolver("testResolver"); + + try (var is = TestUtils.makeInputStream(id, "01002 S000002 00 1970")) { + + resolver.addStream("test.dat", is); + + var parser = new VdypPolygonDescriptionParser().map("test.dat", resolver, controlMap); + + var stream = parser.get(); + + assertTrue(stream.hasNext(), "stream is empty"); + + var ex = assertThrows(ResourceParseException.class, () -> stream.next()); + + assertTrue(stream.hasNext(), "stream is empty"); + + var result = stream.next(); + + assertThat(result, isPolyId("01002 S000002 00", 1970)); + + } + + } + +} From 29ac9989c5b405d4ad47edfa216bede74b5355a2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 24 Oct 2024 11:39:29 -0700 Subject: [PATCH 18/45] Unit test for processor base class --- .../gov/nrs/vdyp/application/Processor.java | 2 +- .../io/parse/model/VdypSpeciesParser.java | 50 ++++++++----- .../nrs/vdyp/application/ProcessorTest.java | 59 +++++++++++++++ .../io/parse/model/VdypSpeciesParserTest.java | 71 +++++++++++++++++++ 4 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java index 347373fde..a6053e727 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/Processor.java @@ -69,7 +69,7 @@ public void run( Path controlFilePath = inputFileResolver.toPath(controlFileName).getParent(); FileSystemFileResolver relativeResolver = new FileSystemFileResolver(controlFilePath); - parser.parse(is, relativeResolver, controlMap); + controlMap = parser.parse(is, relativeResolver, controlMap); } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java index 31398a1a2..4a39b7249 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java @@ -51,6 +51,8 @@ public class VdypSpeciesParser implements ControlMapValueReplacer, EndOfRecord> LAYER_TYPE_EOR_BUILDER = new ValueOrMarker.Builder<>(); + @Override public ControlKey getControlKey() { return ControlKey.FORWARD_INPUT_VDYP_LAYER_BY_SPECIES; @@ -107,11 +109,8 @@ protected ValueOrMarker, EndOfRecord> convert(Map, EndOfRecord>) entry.get(LAYER_TYPE); - if (layerType == null) { - var builder = new ValueOrMarker.Builder, EndOfRecord>(); - layerType = builder.marker(EndOfRecord.END_OF_RECORD); - } + var layerType = (ValueOrMarker, EndOfRecord>) entry + .getOrDefault(LAYER_TYPE, LAYER_TYPE_EOR_BUILDER.marker(EndOfRecord.END_OF_RECORD)); var genusIndex = (Integer) entry.get(GENUS_INDEX); var optionalGenus = (Optional) entry.get(GENUS); var genusNameText0 = (Optional) entry.get(SPECIES_0); @@ -152,20 +151,10 @@ protected ValueOrMarker, EndOfRecord> convert(Map 0.0 && yearsToBreastHeight > 0.0) - iTotalAge = yearsAtBreastHeight + yearsToBreastHeight; - } else if (Float.isNaN(yearsToBreastHeight)) { - if (yearsAtBreastHeight > 0.0 && totalAge > yearsAtBreastHeight) - iYearsToBreastHeight = totalAge - yearsAtBreastHeight; - } + var inferredAges = inferAges(new Ages(totalAge, yearsAtBreastHeight, yearsToBreastHeight)); - var inferredTotalAge = iTotalAge; - var inferredYearsToBreastHeight = iYearsToBreastHeight; + var inferredTotalAge = inferredAges.totalAge; + var inferredYearsToBreastHeight = inferredAges.yearsAtBreastHeight; return VdypSpecies.build(speciesBuilder -> { speciesBuilder.sp64DistributionSet(speciesDistributionSet); @@ -224,6 +213,31 @@ protected boolean stop(ValueOrMarker, EndOfRecord> nextChi }; } + record Ages(float totalAge, float yearsAtBreastHeight, float yearsToBreastHeight) { + } + + /** + * Fills in NaN value for one of totalAge or yearsToBreastHeight if the other values are valid numbers + * + * @param givenAges + * @return An ages object with the NaN value filled in. + */ + static Ages inferAges(final Ages givenAges) { + var iTotalAge = givenAges.totalAge; + var iYearsToBreastHeight = givenAges.yearsToBreastHeight; + + // From VDYPGETS.FOR, lines 235 to 255. + if (Float.isNaN(givenAges.totalAge)) { + if (givenAges.yearsAtBreastHeight > 0.0 && givenAges.yearsToBreastHeight > 0.0) + iTotalAge = givenAges.yearsAtBreastHeight + givenAges.yearsToBreastHeight; + } else if (Float.isNaN(givenAges.yearsToBreastHeight)) { + if (givenAges.yearsAtBreastHeight > 0.0 && givenAges.totalAge > givenAges.yearsAtBreastHeight) + iYearsToBreastHeight = givenAges.totalAge - givenAges.yearsAtBreastHeight; + } + + return new Ages(iTotalAge, givenAges.yearsAtBreastHeight, iYearsToBreastHeight); + } + @Override public ValueParser getValueParser() { return FILENAME; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java new file mode 100644 index 000000000..a77521a37 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java @@ -0,0 +1,59 @@ +package ca.bc.gov.nrs.vdyp.application; + +import java.io.IOException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; + +import org.easymock.EasyMock; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.io.FileResolver; +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.test.MockFileResolver; +import ca.bc.gov.nrs.vdyp.test.TestUtils; + +class ProcessorTest { + + @Test + void test() throws IOException, ResourceParseException, ProcessingException { + + Processor unit = EasyMock.partialMockBuilder(Processor.class).addMockedMethod("getControlFileParser") + .addMockedMethod("process", Set.class, Map.class, Optional.class, Predicate.class).createStrictMock(); + + BaseControlParser controlParser = EasyMock.createMock(BaseControlParser.class); + + var inputResolver = new MockFileResolver("input"); + var outputResolver = new MockFileResolver("output"); + + var is = TestUtils.makeInputStream("TEST"); + + inputResolver.addStream("test.ctr", is); + + var mockMap = new HashMap(); + + EasyMock.expect( + controlParser + .parse(EasyMock.same(is), EasyMock.anyObject(FileResolver.class), EasyMock.anyObject(Map.class)) + ).andStubReturn(mockMap); + + EasyMock.expect(unit.getControlFileParser()).andStubReturn(controlParser); + + unit.process( + EasyMock.eq(EnumSet.allOf(Pass.class)), EasyMock.same(mockMap), EasyMock.anyObject(Optional.class), + EasyMock.anyObject(Predicate.class) + ); + EasyMock.expectLastCall(); + + EasyMock.replay(unit, controlParser); + + unit.run(inputResolver, outputResolver, List.of("test.ctr"), EnumSet.allOf(Pass.class)); + + EasyMock.verify(unit, controlParser); + } +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java index 92ef93b0b..d2a039e30 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java @@ -8,9 +8,13 @@ import java.util.ArrayList; import java.util.NoSuchElementException; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; +import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser.Ages; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.test.MockFileResolver; @@ -179,4 +183,71 @@ void testMultiplePoly() throws IOException, ResourceParseException { } } + @Nested + class InferAges { + + @Test + void testNoNan() { + var result = VdypSpeciesParser.inferAges(new Ages(60, 50, 10)); + assertThat(result, equalTo(new Ages(60, 50, 10))); // Leave as is + } + + @Test + void testTotalNaN() { + var result = VdypSpeciesParser.inferAges(new Ages(Float.NaN, 50, 10)); + assertThat(result, equalTo(new Ages(60, 50, 10))); // Fill in total + } + + @Test + void testYtbNaN() { + var result = VdypSpeciesParser.inferAges(new Ages(60, 50, Float.NaN)); + assertThat(result, equalTo(new Ages(60, 50, 10))); // Fill Years to Breast Height + } + + // TODO maybe implement this the same as the other two + @Test + void testYabNaN() { + var result = VdypSpeciesParser.inferAges(new Ages(60, Float.NaN, 10)); + assertThat(result, equalTo(new Ages(60, Float.NaN, 10))); // Leave as is + } + + // TODO maybe we should log a warning for these cases? + + @Test + void testDontAddUp() { + var result = VdypSpeciesParser.inferAges(new Ages(60, 50, 5)); + assertThat(result, equalTo(new Ages(60, 50, 5))); // Leave as is + } + + @Test + void testTotalAbndYabBothNaN() { + var result = VdypSpeciesParser.inferAges(new Ages(Float.NaN, Float.NaN, 10)); + assertThat(result, equalTo(new Ages(Float.NaN, Float.NaN, 10))); // Leave as is + } + + @Test + void testTotalNaNYtbZero() { + var result = VdypSpeciesParser.inferAges(new Ages(Float.NaN, 50, 0)); + assertThat(result, equalTo(new Ages(Float.NaN, 50, 0))); // Leave as is + } + + @Test + void testTotalNaNYabZero() { + var result = VdypSpeciesParser.inferAges(new Ages(Float.NaN, 0, 10)); + assertThat(result, equalTo(new Ages(Float.NaN, 0, 10))); // Leave as is + } + + @Test + void testYtbNaNTotalZero() { + var result = VdypSpeciesParser.inferAges(new Ages(0, 50, Float.NaN)); + assertThat(result, equalTo(new Ages(0, 50, Float.NaN))); // Leave as is + } + + @Test + void testYtbNaNYabZero() { + var result = VdypSpeciesParser.inferAges(new Ages(60, 0, Float.NaN)); + assertThat(result, equalTo(new Ages(60, 0, Float.NaN))); // Leave as is + } + + } } From 4adebb5c70a071a9fd583034915236a1ae792ef0 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 25 Oct 2024 13:04:52 -0700 Subject: [PATCH 19/45] Unit test for LayerProcessingState --- .../vdyp/back/BackProcessingEngineTest.java | 46 +--- .../application/LayerProcessingStateTest.java | 253 ++++++++++++++++++ .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 4 + .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 45 ++++ 4 files changed, 304 insertions(+), 44 deletions(-) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java index 05be1a747..37f2552c5 100644 --- a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.back; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.compatibilityVariable; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static org.hamcrest.MatcherAssert.assertThat; @@ -37,6 +38,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; class BackProcessingEngineTest { @@ -332,7 +334,6 @@ protected boolean matchesSafely(ComponentSizeLimits item, Description mismatchDe }; } - static Matcher backCV(String name, Object p1, Object p2, Matcher expected) { return compatibilityVariable(name, expected, BackProcessingState.class, p1, p2); } @@ -340,47 +341,4 @@ static Matcher backCV(String name, Object p1, Object p2, Ma static Matcher backCV(String name, Object p1, Matcher expected) { return compatibilityVariable(name, expected, BackProcessingState.class, p1); } - - static Matcher - compatibilityVariable(String name, Matcher expected, Class klazz, Object... params) { - return new TypeSafeDiagnosingMatcher<>(klazz) { - - @Override - public void describeTo(Description description) { - description.appendText(name).appendValueList("(", ", ", ") ", params); - description.appendDescriptionOf(expected); - } - - @Override - protected boolean matchesSafely(T item, Description mismatchDescription) { - Method method; - try { - method = klazz.getMethod( - name, - (Class[]) Arrays.stream(params) - .map(o -> o instanceof Integer ? Integer.TYPE : o.getClass()).toArray(Class[]::new) - ); - } catch (NoSuchMethodException e) { - mismatchDescription.appendText("Method " + name + " does not exist"); - return false; - } - - float result; - try { - result = (float) method.invoke(item, params); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - mismatchDescription.appendText(e.getMessage()); - return false; - } - - if (expected.matches(result)) { - return true; - } - - expected.describeMismatch(result, mismatchDescription); - return false; - } - - }; - } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java new file mode 100644 index 000000000..b68e59e85 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java @@ -0,0 +1,253 @@ +package ca.bc.gov.nrs.vdyp.application; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.compatibilityVariable; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.hamcrest.Matcher; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; +import ca.bc.gov.nrs.vdyp.test.TestUtils; + +class LayerProcessingStateTest { + + static class TestLayerProcessingState extends LayerProcessingState { + + protected TestLayerProcessingState( + ProcessingState ps, VdypPolygon polygon, + LayerType subjectLayerType + ) throws ProcessingException { + super(ps, polygon, subjectLayerType); + } + + @Override + protected Predicate getBankFilter() { + return spec -> true; + } + + @Override + protected void applyCompatibilityVariables(VdypSpecies species, int i) { + // TODO Auto-generated method stub + + } + + @Override + protected VdypLayer updateLayerFromBank() { + // TODO Auto-generated method stub + return null; + } + + } + + @Nested + class Constructor { + + @Test + void testConstructNoSpecies() throws ProcessingException { + var em = EasyMock.createStrictControl(); + ProcessingState parent = em + .createMock("parent", ProcessingState.class); + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + pb.biogeoclimaticZone(TestUtils.mockBec()); + pb.forestInventoryZone("A"); + pb.percentAvailable(90f); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + + }); + }); + + em.replay(); + + var unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY); + + assertThat(unit, hasProperty("NSpecies", is(0))); + + em.verify(); + + } + + @Test + void testConstructOneSpecies() throws ProcessingException { + var em = EasyMock.createStrictControl(); + ProcessingState parent = em + .createMock("parent", ProcessingState.class); + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + pb.biogeoclimaticZone(TestUtils.mockBec()); + pb.forestInventoryZone("A"); + pb.percentAvailable(90f); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + + lb.addSpecies(sb -> { + sb.genus("A", 1); + }); + }); + }); + + em.replay(); + + var unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY); + + assertThat(unit, hasProperty("NSpecies", is(1))); + + em.verify(); + + } + } + + @Nested + class SetCompatibilityVariables { + IMocksControl em; + LayerProcessingState unit; + + MatrixMap3[] cvVolume; + MatrixMap2[] cvBa; + MatrixMap2[] cvDq; + Map[] cvSm; + + @BeforeEach + @SuppressWarnings("unchecked") + void setup() throws ProcessingException { + var em = EasyMock.createStrictControl(); + ProcessingState parent = em + .createMock("parent", ProcessingState.class); + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + pb.biogeoclimaticZone(TestUtils.mockBec()); + pb.forestInventoryZone("A"); + pb.percentAvailable(90f); + + pb.addLayer(lb -> { + lb.layerType(LayerType.PRIMARY); + + lb.addSpecies(sb -> { + sb.genus("A", 1); + }); + }); + }); + + em.replay(); + + unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY); + + cvVolume = new MatrixMap3[] { null, new MatrixMap3Impl( + List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), List.of(LayerType.values()), + (uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + + cvBa = new MatrixMap2[] { null, + new MatrixMap2Impl( + List.of(UtilizationClass.values()), List.of(LayerType.values()), + (uc, lt) -> 13f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + + cvDq = new MatrixMap2[] { null, + new MatrixMap2Impl( + List.of(UtilizationClass.values()), List.of(LayerType.values()), + (uc, lt) -> 17f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; + cvSm = new EnumMap[] { null, new EnumMap(UtilizationClassVariable.class) }; + + for (var uc : UtilizationClassVariable.values()) { + cvSm[1].put(uc, uc.ordinal() * 7f); + } + + } + + @Test + void testFailBeforeSet() throws ProcessingException { + assertThrows( + IllegalStateException.class, + () -> unit.getCVVolume(1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY), + "getCVVolume" + ); + assertThrows( + IllegalStateException.class, () -> unit.getCVBasalArea(1, UtilizationClass.ALL, LayerType.PRIMARY), + "getCVBasalArea" + ); + assertThrows( + IllegalStateException.class, + () -> unit.getCVQuadraticMeanDiameter(1, UtilizationClass.ALL, LayerType.PRIMARY), + "getCVQuadraticMeanDiameter" + ); + assertThrows( + IllegalStateException.class, () -> unit.getCVSmall(1, UtilizationClassVariable.BASAL_AREA), + "getCVSmall" + ); + } + + @Test + void testSucceedAfterSet() throws ProcessingException { + unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm); + assertThat( + unit, + testCV( + "getCVVolume", 1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, + is(16f) + ) + ); + assertThat(unit, testCV("getCVBasalArea", 1, UtilizationClass.ALL, LayerType.PRIMARY, is(16f))); + assertThat( + unit, + testCV("getCVQuadraticMeanDiameter", 1, UtilizationClass.U125TO175, LayerType.PRIMARY, is(26f)) + ); + assertThat(unit, testCV("getCVSmall", 1, UtilizationClassVariable.QUAD_MEAN_DIAMETER, is(7f))); + } + + @Test + void testFailDoubleSet() throws ProcessingException { + unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm); + assertThrows( + IllegalStateException.class, () -> unit.setCompatibilityVariableDetails(cvVolume, cvBa, cvDq, cvSm) + ); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Matcher> + testCV(String name, Object p1, Object p2, Object p3, Object p4, Matcher match) { + return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2, p3, p4); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Matcher> testCV(String name, Object p1, Object p2, Object p3, Matcher match) { + return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2, p3); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Matcher> testCV(String name, Object p1, Object p2, Matcher match) { + return (Matcher) compatibilityVariable(name, match, LayerProcessingState.class, p1, p2); + } + + } +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 11491ef43..d63e90f91 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -634,4 +634,8 @@ public static BiFunction> netDecayMap(i return layer.getSpecies().get(ids[0]); } + + public static BecDefinition mockBec() { + return new BecDefinition("T", Region.COASTAL, "Test"); + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 92c70aa13..ced962a41 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -14,6 +14,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -693,4 +695,47 @@ protected boolean matchesSafely(Coefficients item, Description mismatchDescripti }; } + public static Matcher + compatibilityVariable(String name, Matcher expected, Class klazz, Object... params) { + return new TypeSafeDiagnosingMatcher<>(klazz) { + + @Override + public void describeTo(Description description) { + description.appendText(name).appendValueList("(", ", ", ") ", params); + description.appendDescriptionOf(expected); + } + + @Override + protected boolean matchesSafely(T item, Description mismatchDescription) { + Method method; + try { + method = klazz.getMethod( + name, + (Class[]) Arrays.stream(params) + .map(o -> o instanceof Integer ? Integer.TYPE : o.getClass()).toArray(Class[]::new) + ); + } catch (NoSuchMethodException e) { + mismatchDescription.appendText("Method " + name + " does not exist"); + return false; + } + + float result; + try { + result = (float) method.invoke(item, params); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + mismatchDescription.appendText(e.getMessage()); + return false; + } + + if (expected.matches(result)) { + return true; + } + + expected.describeMismatch(result, mismatchDescription); + return false; + } + + }; + } + } From 4ae7c0ef37f1e7c7088c38530d3e45a1412ed1ed Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 25 Oct 2024 13:31:44 -0700 Subject: [PATCH 20/45] Fix typo in age inference --- .../java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java | 1 + .../ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java index 37f2552c5..fe741ae79 100644 --- a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -334,6 +334,7 @@ protected boolean matchesSafely(ComponentSizeLimits item, Description mismatchDe }; } + static Matcher backCV(String name, Object p1, Object p2, Matcher expected) { return compatibilityVariable(name, expected, BackProcessingState.class, p1, p2); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java index 4a39b7249..6e278577e 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java @@ -154,7 +154,7 @@ protected ValueOrMarker, EndOfRecord> convert(Map { speciesBuilder.sp64DistributionSet(speciesDistributionSet); From 96eeb56fc211f6e83bb1d76fc56b16371afa25fb Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 28 Oct 2024 11:01:48 -0700 Subject: [PATCH 21/45] Unit test for building a layer from a bank --- .../nrs/vdyp/processing_state/BankTest.java | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index 89aac59c1..2cf7951bf 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -54,18 +54,22 @@ void before() throws IOException, ResourceParseException { lb.addSpecies(sb -> { sb.genus("B", controlMap); sb.baseArea(0.4f); + sb.percentGenus(10); }); lb.addSpecies(sb -> { sb.genus("C", controlMap); sb.baseArea(0.6f); + sb.percentGenus(10); }); lb.addSpecies(sb -> { sb.genus("D", controlMap); sb.baseArea(10f); + sb.percentGenus(10); }); lb.addSpecies(sb -> { sb.genus("H", controlMap); sb.baseArea(50f); + sb.percentGenus(60); sb.addSite(ib -> { ib.ageTotal(100); ib.yearsToBreastHeight(5); @@ -77,6 +81,7 @@ void before() throws IOException, ResourceParseException { lb.addSpecies(sb -> { sb.genus("S", controlMap); sb.baseArea(99.9f); + sb.percentGenus(10); sb.addSite(ib -> { ib.ageTotal(100); ib.yearsToBreastHeight(5); @@ -242,6 +247,46 @@ void testLayerUpdate() throws IOException, ResourceParseException, ProcessingExc verifyBankMatchesLayer(bank, pLayer); } + @Test + void testBuildLayerFromBank() throws IOException, ResourceParseException, ProcessingException { + + VdypLayer pLayer = polygon.getLayers().get(LayerType.PRIMARY); + assertThat(pLayer, notNullValue()); + + Bank bank = new Bank(pLayer, polygon.getBiogeoclimaticZone(), s -> true); + + pLayer = ProcessingTestUtils.normalizeLayer(pLayer); + + verifyBankMatchesLayer(bank, pLayer); + + var bankPart2 = new float[][][] { bank.loreyHeights, bank.basalAreas, bank.quadMeanDiameters, + bank.treesPerHectare, bank.wholeStemVolumes, bank.closeUtilizationVolumes, bank.cuVolumesMinusDecay, + bank.cuVolumesMinusDecayAndWastage }; + + var bankPart1 = new float[][] { bank.ageTotals, bank.dominantHeights, + // bank.percentagesOfForestedLand, + bank.siteIndices, bank.yearsAtBreastHeight + // bank.yearsToBreastHeight // + }; + + for (int i = 0; i < bankPart1.length; i++) { + for (int j = 0; j < bankPart1[i].length; j++) { + bankPart1[i][j] += 1; + } + } + for (int i = 0; i < bankPart2.length; i++) { + for (int j = 0; j < bankPart2[i].length; j++) { + for (int k = 0; k < bankPart2[i][j].length; k++) { + bankPart2[i][j][k] += 1; + } + } + } + + var result = bank.buildLayerFromBank(); + + verifyBankMatchesLayer(bank, result); + } + private void verifyBankMatchesLayer(Bank lps, VdypLayer layer) { List sortedSpIndices = layer.getSpecies().values().stream().map(s -> s.getGenusIndex()).sorted() @@ -293,11 +338,17 @@ private void verifyBankSpeciesMatchesSpecies(Bank bank, int index, VdypSpecies s assertThat(bank.speciesNames[index], is(species.getGenus())); species.getSite().ifPresentOrElse(site -> { - assertThat(bank.yearsAtBreastHeight[index], is(site.getYearsAtBreastHeight().get())); - assertThat(bank.ageTotals[index], is(site.getAgeTotal().get())); - assertThat(bank.dominantHeights[index], is(site.getHeight().get())); - assertThat(bank.siteIndices[index], is(site.getSiteIndex().get())); - assertThat(bank.yearsToBreastHeight[index], is(site.getYearsToBreastHeight().get())); + assertThat( + bank.yearsAtBreastHeight[index], + is(site.getYearsAtBreastHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE)) + ); + assertThat(bank.ageTotals[index], is(site.getAgeTotal().orElse(VdypEntity.MISSING_FLOAT_VALUE))); + assertThat(bank.dominantHeights[index], is(site.getHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE))); + assertThat(bank.siteIndices[index], is(site.getSiteIndex().orElse(VdypEntity.MISSING_FLOAT_VALUE))); + assertThat( + bank.yearsToBreastHeight[index], + is(site.getYearsToBreastHeight().orElse(VdypEntity.MISSING_FLOAT_VALUE)) + ); site.getSiteCurveNumber().ifPresentOrElse(scn -> { assertThat(bank.siteCurveNumbers[index], is(scn)); }, () -> { From 138043dc61278bb7efcda8de312a7c9876cad1a2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 28 Oct 2024 11:54:16 -0700 Subject: [PATCH 22/45] Cleanign up warnings --- .../nrs/vdyp/back/BackProcessingEngineTest.java | 4 ---- .../application/VdypProcessingApplication.java | 4 +--- .../vdyp/io/parse/model/VdypUtilizationParser.java | 2 +- .../gov/nrs/vdyp/io/parse/value/ValueParser.java | 3 ++- .../bc/gov/nrs/vdyp/application/ProcessorTest.java | 1 + .../application/VdypProcessingApplicationTest.java | 8 ++++---- .../model/VdypPolygonDescriptionParserTest.java | 5 ----- .../vdyp/io/parse/model/VdypPolygonParserTest.java | 5 ----- .../vdyp/io/parse/model/VdypSpeciesParserTest.java | 3 --- .../bc/gov/nrs/vdyp/processing_state/BankTest.java | 3 --- .../LayerProcessingStateTest.java | 14 +++++++++----- .../nrs/vdyp/forward/test/ForwardTestUtils.java | 6 ------ 12 files changed, 18 insertions(+), 40 deletions(-) rename lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/{application => processing_state}/LayerProcessingStateTest.java (96%) diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java index fe741ae79..d1693c1d4 100644 --- a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -6,9 +6,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -38,7 +35,6 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; -import ca.bc.gov.nrs.vdyp.test.VdypMatchers; class BackProcessingEngineTest { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java index 0e700731a..1ec7999de 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplication.java @@ -74,8 +74,6 @@ public int run(final PrintStream os, final InputStream is, final String... args) processor.run(new FileSystemFileResolver(), new FileSystemFileResolver(), controlFileNames, getAllPasses()); - System.err.println("Blah"); - } catch (Exception ex) { logger.error("Error during processing", ex); return PROCESSING_ERROR_EXIT; @@ -90,7 +88,7 @@ public List getControlFileNamesFromUser(final PrintStream os, final Inpu List controlFileNames; os.print( MessageFormat - .format("Enter name of control file (or RETURN for {1}) or *name for both): ", defaultFilename) + .format("Enter name of control file (or RETURN for {0}) or *name for both): ", defaultFilename) ); controlFileNames = new ArrayList<>(); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java index d9857215c..57b5035c5 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypUtilizationParser.java @@ -64,7 +64,7 @@ public ControlKey getControlKey() { ) ).value(3, GENUS_INDEX, ValueParser.INTEGER) // .space(1) // - .value(2, GENUS, ControlledValueParser.optional(ValueParser.GENUS)) + .value(2, GENUS, ControlledValueParser.optional(ControlledValueParser.GENUS)) .value(3, UTILIZATION_CLASS_INDEX, ControlledValueParser.UTILIZATION_CLASS) .value(9, BASAL_AREA, ValueParser.FLOAT_WITH_DEFAULT) .value(9, LIVE_TREES_PER_HECTARE, ValueParser.FLOAT_WITH_DEFAULT) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java index 87b59fd6f..2753a7247 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/value/ValueParser.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.io.parse.value; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -71,7 +72,7 @@ static > ValueParser rangeSilentWithDefaulting( if (!defaultValue.equals(result) && (result.compareTo(max) > (includeMax ? 0 : -1) || result.compareTo(min) < (includeMin ? 0 : 1))) { return Optional.of( - String.format( + MessageFormat.format( "{} must be between {} ({}) and {} ({})", name, min, includeMin ? "inclusive" : "exclusive", max, includeMax ? "inclusive" : "exclusive" ) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java index a77521a37..a1bd20145 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/ProcessorTest.java @@ -20,6 +20,7 @@ class ProcessorTest { + @SuppressWarnings("unchecked") @Test void test() throws IOException, ResourceParseException, ProcessingException { diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java index 2ff829e0d..164a84580 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypProcessingApplicationTest.java @@ -24,14 +24,14 @@ class VdypProcessingApplicationTest { @Nested class Run { - VdypProcessingApplication app; + VdypProcessingApplication app; Processor processor; @BeforeEach void init() { processor = EasyMock.createMock(Processor.class); - app = new VdypProcessingApplication() { + app = new VdypProcessingApplication() { @Override public String getDefaultControlFileName() { @@ -154,11 +154,11 @@ void testErrorWhileProcessing() throws Exception { @Nested class GetControlFileNamesFromUser { - VdypProcessingApplication app; + VdypProcessingApplication app; @BeforeEach void init() { - app = new VdypProcessingApplication() { + app = new VdypProcessingApplication() { @Override public String getDefaultControlFileName() { diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java index 6c510617d..f6b8082f8 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonDescriptionParserTest.java @@ -5,20 +5,15 @@ import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; -import java.util.ArrayList; import java.util.NoSuchElementException; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; -import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.test.MockFileResolver; import ca.bc.gov.nrs.vdyp.test.TestUtils; -import ca.bc.gov.nrs.vdyp.test.VdypMatchers; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java index 221d39d9a..6e19d5d43 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypPolygonParserTest.java @@ -5,18 +5,13 @@ import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; -import java.util.ArrayList; import java.util.NoSuchElementException; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; -import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.test.MockFileResolver; import ca.bc.gov.nrs.vdyp.test.TestUtils; -import ca.bc.gov.nrs.vdyp.test.VdypMatchers; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java index d2a039e30..9a68c5fb3 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParserTest.java @@ -8,15 +8,12 @@ import java.util.ArrayList; import java.util.NoSuchElementException; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.model.VdypSpeciesParser.Ages; import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.test.MockFileResolver; import ca.bc.gov.nrs.vdyp.test.TestUtils; import ca.bc.gov.nrs.vdyp.test.VdypMatchers; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index 2cf7951bf..df05370dc 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -8,9 +8,6 @@ import java.util.List; import java.util.Map; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeDiagnosingMatcher; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java similarity index 96% rename from lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java rename to lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java index b68e59e85..65dcbb3e8 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/LayerProcessingStateTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.application; +package ca.bc.gov.nrs.vdyp.processing_state; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.compatibilityVariable; import static org.hamcrest.MatcherAssert.assertThat; @@ -14,11 +14,12 @@ import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.hamcrest.Matcher; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; @@ -31,8 +32,6 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; -import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; -import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; import ca.bc.gov.nrs.vdyp.test.TestUtils; class LayerProcessingStateTest { @@ -139,7 +138,7 @@ class SetCompatibilityVariables { @BeforeEach @SuppressWarnings("unchecked") void setup() throws ProcessingException { - var em = EasyMock.createStrictControl(); + em = EasyMock.createStrictControl(); ProcessingState parent = em .createMock("parent", ProcessingState.class); var polygon = VdypPolygon.build(pb -> { @@ -185,6 +184,11 @@ void setup() throws ProcessingException { } + @AfterEach + void cleanup() { + em.verify(); + } + @Test void testFailBeforeSet() throws ProcessingException { assertThrows( diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java index e02234555..e4b248c8d 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java @@ -3,14 +3,12 @@ import java.io.IOException; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.function.BiFunction; import org.opentest4j.AssertionFailedError; import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; -import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; import ca.bc.gov.nrs.vdyp.forward.ForwardControlParser; import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingState; import ca.bc.gov.nrs.vdyp.forward.model.ControlVariable; @@ -19,10 +17,6 @@ import ca.bc.gov.nrs.vdyp.io.parse.control.BaseControlParser; import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParseException; import ca.bc.gov.nrs.vdyp.model.Region; -import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationVector; -import ca.bc.gov.nrs.vdyp.model.VdypLayer; -import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.test.TestUtils; public class ForwardTestUtils { From c260fd741acbb3b1b8e21dca85463b58310bd08d Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 31 Oct 2024 11:49:07 -0700 Subject: [PATCH 23/45] Moving executiuon step to general processing engine class --- .../nrs/vdyp/back/BackProcessingEngine.java | 7 ++ .../vdyp/application/ProcessingEngine.java | 95 ++++++++++++++++++- .../bc/gov/nrs/vdyp/forward/ForwardPass.java | 20 ---- .../vdyp/forward/ForwardProcessingEngine.java | 84 +--------------- .../forward/Grow10StoreSpeciesDetails.java | 2 +- .../Grow11UpdateCompatibilityVariables.java | 2 +- ...Grow1CalculateDominantHeightDeltaTest.java | 2 +- .../Grow2CalculateBasalAreaDeltaTest.java | 2 +- ...ow3CalculateQuadMeanDiameterDeltaTest.java | 2 +- ...row4CalculateLoreyHeightEstimatesTest.java | 2 +- .../vdyp/forward/Grow5SpeciesBaDqTphTest.java | 2 +- .../forward/Grow6TreesPerHectareTest.java | 2 +- .../forward/Grow7QuadMeanDiameterTest.java | 2 +- .../Grow8PerSpeciesLoreyHeightTest.java | 2 +- .../Grow9PercentagesOfForestedLand.java | 2 +- .../nrs/vdyp/forward/GrowAllStepsTest.java | 3 +- ...inaryForwardProcessingEngineStepsTest.java | 21 ++-- ...liminarySetCompatibilityVariablesTest.java | 3 +- .../vdyp/forward/ProcessPolygonBasicTest.java | 2 +- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 3 +- 20 files changed, 132 insertions(+), 128 deletions(-) delete mode 100644 lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardPass.java diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java index 30ef4433e..b15fc2550 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -18,6 +18,7 @@ import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; @@ -118,4 +119,10 @@ void prepare(BackProcessingState state) throws ProcessingException { state.setFinalQuadMeanDiameters(finalDiameters); } + + @Override + public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) throws ProcessingException { + // TODO Auto-generated method stub + + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java index 225e00074..c357bfc6e 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java @@ -1,5 +1,98 @@ package ca.bc.gov.nrs.vdyp.application; -public class ProcessingEngine { +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; + +public abstract class ProcessingEngine { + + /** + * Run all steps of the engine on the given polygon up to and including the given lastStep. + * + * @param polygon the polygon on which to operate + * @param lastStepInclusive execute up to and including this step + * + * @throws ProcessingException should an error with the data occur during processing + */ + public abstract void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) + throws ProcessingException; + + public enum ExecutionStep { + // Must be first + NONE, // + + CHECK_FOR_WORK, // + CALCULATE_MISSING_SITE_CURVES, // + CALCULATE_COVERAGES, // + DETERMINE_POLYGON_RANKINGS, // + ESTIMATE_MISSING_SITE_INDICES, // + ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES, // + CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX, // + SET_COMPATIBILITY_VARIABLES, // + GROW_1_LAYER_DHDELTA, // + GROW_2_LAYER_BADELTA, // + GROW_3_LAYER_DQDELTA, // + GROW_4_LAYER_BA_AND_DQTPH_EST, // + GROW_5A_LH_EST, // + GROW_5_SPECIES_BADQTPH, // + GROW_6_LAYER_TPH2, // + GROW_7_LAYER_DQ2, // + GROW_8_SPECIES_LH, // + GROW_9_SPECIES_PCT, // + GROW_10_COMPATIBILITY_VARS, // + GROW_11_SPECIES_UC, // + GROW_12_SPECIES_UC_SMALL, // + GROW_13_STORE_SPECIES_DETAILS, // + GROW, // + + // Must be last + ALL; // + + public ExecutionStep predecessor() { + if (this == NONE) { + throw new IllegalStateException("ExecutionStep.None has no predecessor"); + } + + return ExecutionStep.values()[ordinal() - 1]; + } + + public ExecutionStep successor() { + if (this == ALL) { + throw new IllegalStateException("ExecutionStep.All has no successor"); + } + + return ExecutionStep.values()[ordinal() + 1]; + } + + public boolean lt(ExecutionStep that) { + return this.ordinal() < that.ordinal(); + } + + public boolean le(ExecutionStep that) { + return this.ordinal() <= that.ordinal(); + } + + public boolean eq(ExecutionStep that) { + return this.ordinal() == that.ordinal(); + } + + public boolean ge(ExecutionStep that) { + return this.ordinal() >= that.ordinal(); + } + + public boolean gt(ExecutionStep that) { + return this.ordinal() > that.ordinal(); + } + } + + /** + * Run all steps of the engine on the given polygon. + * + * @param polygon the polygon on which to operate + * + * @throws ProcessingException should an error with the data occur during processing + */ + public void processPolygon(VdypPolygon polygon) throws ProcessingException { + + processPolygon(polygon, ExecutionStep.ALL); + } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardPass.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardPass.java deleted file mode 100644 index 56fd82e0d..000000000 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardPass.java +++ /dev/null @@ -1,20 +0,0 @@ -package ca.bc.gov.nrs.vdyp.forward; - -// -// VDYPPASS IN/OUT I*4(10) Major Control Functions -// (1) IN Perform Initiation activities? (0=No, 1=Yes) -// (2) IN Open the stand data files (0=No, 1=Yes) -// (3) IN Process stands (0=No, 1=Yes) -// (4) IN Allow multiple polygons (0=No, 1=Yes) -// (Subset of stand processing. May limit to 1 stand) -// (5) IN CLOSE data files. -// (10) OUT Indicator variable that in the case of single stand processing -// with VDYPPASS(4) set, behaves as follows: -// -100 due to EOF, nothing to read -// other -ve value, incl -99. Could not process the stand. -// 0 Stand was processed and written -// +ve value. Serious error. Set to IER. - -public enum ForwardPass { - PASS_1, PASS_2, PASS_3, PASS_4, PASS_5, PASS_6 -} diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index 2421dc74e..d5e3b0034 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -21,6 +21,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.application.RuntimeProcessingException; import ca.bc.gov.nrs.vdyp.application.StandProcessingException; @@ -74,7 +75,7 @@ * processPolygon for each polygon to be processed. All calls to processPolygon are entirely * independent of one another, allowing (different) polygons to the processed in parallel. */ -public class ForwardProcessingEngine { +public class ForwardProcessingEngine extends ProcessingEngine { private static final Logger logger = LoggerFactory.getLogger(ForwardProcessor.class); @@ -106,86 +107,6 @@ public ForwardProcessingEngine(Map controlMap) throws Processing this(controlMap, Optional.empty()); } - public enum ExecutionStep { - // Must be first - NONE, // - - CHECK_FOR_WORK, // - CALCULATE_MISSING_SITE_CURVES, // - CALCULATE_COVERAGES, // - DETERMINE_POLYGON_RANKINGS, // - ESTIMATE_MISSING_SITE_INDICES, // - ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES, // - CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX, // - SET_COMPATIBILITY_VARIABLES, // - GROW_1_LAYER_DHDELTA, // - GROW_2_LAYER_BADELTA, // - GROW_3_LAYER_DQDELTA, // - GROW_4_LAYER_BA_AND_DQTPH_EST, // - GROW_5A_LH_EST, // - GROW_5_SPECIES_BADQTPH, // - GROW_6_LAYER_TPH2, // - GROW_7_LAYER_DQ2, // - GROW_8_SPECIES_LH, // - GROW_9_SPECIES_PCT, // - GROW_10_COMPATIBILITY_VARS, // - GROW_11_SPECIES_UC, // - GROW_12_SPECIES_UC_SMALL, // - GROW_13_STORE_SPECIES_DETAILS, // - GROW, // - - // Must be last - ALL; // - - public ExecutionStep predecessor() { - if (this == NONE) { - throw new IllegalStateException("ExecutionStep.None has no predecessor"); - } - - return ExecutionStep.values()[ordinal() - 1]; - } - - public ExecutionStep successor() { - if (this == ALL) { - throw new IllegalStateException("ExecutionStep.All has no successor"); - } - - return ExecutionStep.values()[ordinal() + 1]; - } - - public boolean lt(ExecutionStep that) { - return this.ordinal() < that.ordinal(); - } - - public boolean le(ExecutionStep that) { - return this.ordinal() <= that.ordinal(); - } - - public boolean eq(ExecutionStep that) { - return this.ordinal() == that.ordinal(); - } - - public boolean ge(ExecutionStep that) { - return this.ordinal() >= that.ordinal(); - } - - public boolean gt(ExecutionStep that) { - return this.ordinal() > that.ordinal(); - } - } - - /** - * Run all steps of the engine on the given polygon. - * - * @param polygon the polygon on which to operate - * - * @throws ProcessingException should an error with the data occur during processing - */ - public void processPolygon(VdypPolygon polygon) throws ProcessingException { - - processPolygon(polygon, ExecutionStep.ALL); - } - /** * Run all steps of the engine on the given polygon up to and including the given lastStep. * @@ -194,6 +115,7 @@ public void processPolygon(VdypPolygon polygon) throws ProcessingException { * * @throws ProcessingException should an error with the data occur during processing */ + @Override public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) throws ProcessingException { logger.info("Starting processing of the primary layer of polygon {}", polygon.getPolygonIdentifier()); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java index ca2fcaa0d..7514a16c3 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java @@ -12,9 +12,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index 242fa8175..66ab97818 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java index b1a0ec476..1c16d665c 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java @@ -27,7 +27,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java index b7568c584..4783f7a53 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java @@ -14,7 +14,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java index 411818dd4..c3b947bb5 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java @@ -16,7 +16,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Reference; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java index c5b40e4b6..38453bc0e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java index 33a37ad85..615faa5fb 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java index 1c6843669..4bfacd730 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java index e42870fb0..aae409868 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java index 2d084e1ee..5d9cdd4c2 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java @@ -13,7 +13,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java index e853dbb4d..78fac20a9 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java @@ -14,7 +14,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/GrowAllStepsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/GrowAllStepsTest.java index 432f3adf7..c90a3a47a 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/GrowAllStepsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/GrowAllStepsTest.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; @@ -68,7 +69,7 @@ private class WorkCompletedException extends Exception { @Test void testStandardPath() throws ProcessingException, ValueParseException { - ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); + ProcessingEngine fpe = new ForwardProcessingEngine(controlMap); try { var nextComparisonPolygonRef = comparisonDataStreamReader.readNextPolygon(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java index 3a8fddd0d..a8bed32cf 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java @@ -14,6 +14,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common_calculators.custom_exceptions.CurveErrorException; @@ -129,7 +130,7 @@ void testGroupAndStratumNumberSpecialCases() throws IOException, ResourceParseEx var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.DETERMINE_POLYGON_RANKINGS); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.DETERMINE_POLYGON_RANKINGS); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesIndex(), is(1)); assertThrows( @@ -159,7 +160,7 @@ void testCalculateMissingSiteCurves() throws IOException, ResourceParseException var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -195,7 +196,7 @@ void testCalculateMissingSiteCurvesNoSiteCurveData() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -235,7 +236,7 @@ void testEstimateMissingSiteIndicesStep1() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); // Despite 13.40 being in the data stream, the change (2024/8/29) to ignore site information // for all species of the layer except the primary means that method (1) will never be @@ -282,7 +283,7 @@ void testEstimateMissingSiteIndicesStep2() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); var sourceSiteCurve = SiteIndexEquation.SI_CWC_BARKER; var sourceSiteIndex = 13.4f; @@ -313,9 +314,7 @@ void testEstimateMissingYearsToBreastHeightValues() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon( - polygon, ForwardProcessingEngine.ExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES - ); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES); assertThat( fpe.fps.getPrimaryLayerProcessingState().getBank().yearsToBreastHeight, @@ -346,11 +345,11 @@ void testCalculateDominantHeightAgeSiteIndex() throws ProcessingException, IOExc // Since the change to ignore site information for all but non-primary species, there is // no way to successfully estimate age for a primary species from the non-primary species. - ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); + ProcessingEngine fpe = new ForwardProcessingEngine(controlMap); assertThrows( ProcessingException.class, () -> fpe.processPolygon( - polygon, ForwardProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + polygon, ProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX ) ); } @@ -388,7 +387,7 @@ void testCalculateDominantHeightAgeSiteIndexNoSecondary() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesDominantHeight(), is(22.950302f)); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesSiteIndex(), is(34.0f)); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java index 4f64170b4..58349eda9 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.model.LayerType; @@ -40,7 +41,7 @@ void testSetCompatibilityVariables() throws ResourceParseException, IOException, var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ExecutionStep.SET_COMPATIBILITY_VARIABLES); + fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.SET_COMPATIBILITY_VARIABLES); // These values have been verified against the FORTRAN implementation, allowing for minor // platform-specific differences. diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java index b8dfe6f26..1f27c6ac9 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java @@ -11,7 +11,7 @@ import org.slf4j.LoggerFactory; import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; class ProcessPolygonBasicTest extends AbstractForwardProcessingEngineTest { diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index c71467020..74db7b0e6 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -1517,7 +1517,8 @@ float findRootForQuadMeanDiameterFractionalError( // Note, this function has side effects in that it modifies resultPerSpecies. This is intentional, the goal is // to apply adjustment factor x to the values in initialDqs until the combination of their values has minimal - // error then use those adjusted values. + // error then use those adjusted values. x is computed such that it is 0 when the sum of TPH for the species + // is equal to the expected TPH for the layer and the root finder tries to bring it to 0. // Keeping track of the recent X values tied can be used to make some sort of guess if it doesn't converge. double[] lastXes = new double[2]; From e1d43af8655770288455b8275d291a3854f14da8 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 5 Nov 2024 19:40:16 -0800 Subject: [PATCH 24/45] Refactoring --- .../nrs/vdyp/back/BackProcessingEngine.java | 36 ++- .../vdyp/back/BackProcessingEngineTest.java | 21 +- .../vdyp/application/ProcessingEngine.java | 90 ++---- .../java/ca/bc/gov/nrs/vdyp/common/Utils.java | 66 ++++ .../io/parse/model/VdypSpeciesParser.java | 5 +- .../bc/gov/nrs/vdyp/model/BaseVdypLayer.java | 22 +- .../gov/nrs/vdyp/model/BaseVdypPolygon.java | 19 +- .../bc/gov/nrs/vdyp/model/BaseVdypSite.java | 31 +- .../gov/nrs/vdyp/model/BaseVdypSpecies.java | 68 ++-- .../model/VdypCompatibilityVariables.java | 159 ++++++++++ .../ca/bc/gov/nrs/vdyp/model/VdypLayer.java | 90 ++---- .../ca/bc/gov/nrs/vdyp/model/VdypPolygon.java | 4 + .../ca/bc/gov/nrs/vdyp/model/VdypSpecies.java | 138 +++------ .../nrs/vdyp/model/VdypUtilizationHolder.java | 105 +++++++ .../builders/LayerIdentifiedBuilder.java | 11 + .../{ => builders}/ModelClassBuilder.java | 2 +- .../builders/PolygonIdentifiedBuilder.java | 18 ++ .../SpeciesGroupIdentifiedBuilder.java | 8 + .../gov/nrs/vdyp/processing_state/Bank.java | 4 +- .../application/VdypStartApplicationTest.java | 130 +++++--- .../vdyp/application/test/TestSpecies.java | 2 +- .../vdyp/common/EstimationMethodsTest.java | 27 +- .../ca/bc/gov/nrs/vdyp/common/UtilsTest.java | 8 +- .../vdyp/io/write/VdypOutputWriterTest.java | 24 +- .../bc/gov/nrs/vdyp/model/VdypLayerTest.java | 35 ++- .../bc/gov/nrs/vdyp/model/VdypSiteTest.java | 10 +- .../gov/nrs/vdyp/model/VdypSpeciesTest.java | 24 +- .../nrs/vdyp/processing_state/BankTest.java | 23 +- .../LayerProcessingStateTest.java | 6 +- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 90 ++++++ .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 2 +- .../bc/gov/nrs/vdyp/fip/FipLayerParser.java | 4 +- .../bc/gov/nrs/vdyp/fip/FipSpeciesParser.java | 3 +- .../bc/gov/nrs/vdyp/fip/model/FipSpecies.java | 2 +- .../ca/bc/gov/nrs/vdyp/fip/FipStartTest.java | 144 ++++++--- .../bc/gov/nrs/vdyp/fip/RootFinderTest.java | 15 +- .../vdyp/forward/ForwardProcessingEngine.java | 130 +++++--- .../forward/Grow10StoreSpeciesDetails.java | 2 +- .../Grow11UpdateCompatibilityVariables.java | 3 +- ...Grow1CalculateDominantHeightDeltaTest.java | 3 +- .../Grow2CalculateBasalAreaDeltaTest.java | 7 +- ...ow3CalculateQuadMeanDiameterDeltaTest.java | 13 +- ...row4CalculateLoreyHeightEstimatesTest.java | 5 +- .../vdyp/forward/Grow5SpeciesBaDqTphTest.java | 7 +- .../forward/Grow6TreesPerHectareTest.java | 3 +- .../forward/Grow7QuadMeanDiameterTest.java | 3 +- .../Grow8PerSpeciesLoreyHeightTest.java | 5 +- .../Grow9PercentagesOfForestedLand.java | 3 +- ...inaryForwardProcessingEngineStepsTest.java | 20 +- ...liminarySetCompatibilityVariablesTest.java | 2 +- .../vdyp/forward/ProcessPolygonBasicTest.java | 2 +- .../forward/UtilizationOperationsTest.java | 18 +- .../ca/bc/gov/nrs/vdyp/vri/VriSiteParser.java | 2 +- .../bc/gov/nrs/vdyp/vri/VriSpeciesParser.java | 3 +- .../java/ca/bc/gov/nrs/vdyp/vri/VriStart.java | 10 +- .../bc/gov/nrs/vdyp/vri/model/VriSpecies.java | 2 +- .../gov/nrs/vdyp/vri/ParsersTogetherTest.java | 55 ++-- .../nrs/vdyp/vri/VriInputValidationTest.java | 147 ++++++--- .../ca/bc/gov/nrs/vdyp/vri/VriStartTest.java | 293 ++++++++++++------ 59 files changed, 1444 insertions(+), 740 deletions(-) create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/LayerIdentifiedBuilder.java rename lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/{ => builders}/ModelClassBuilder.java (97%) create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/PolygonIdentifiedBuilder.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/SpeciesGroupIdentifiedBuilder.java diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java index b15fc2550..88cf79674 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -11,6 +11,7 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.application.StandProcessingException; import ca.bc.gov.nrs.vdyp.back.processing_state.BackProcessingState; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; @@ -22,7 +23,28 @@ import ca.bc.gov.nrs.vdyp.model.VolumeVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; -public class BackProcessingEngine extends ProcessingEngine { +public class BackProcessingEngine extends ProcessingEngine { + + public enum BackExecutionStep implements ProcessingEngine.ExecutionStep { + // Must be first + NONE, // + + GROW, // + + // Must be last + ALL; // + + @Override + public BackExecutionStep predecessor() throws IllegalStateException { + return Utils.predecessorOrThrow(this, BackExecutionStep.values()); + } + + @Override + public BackExecutionStep successor() { + return Utils.successorOrThrow(this, BackExecutionStep.values()); + } + + } /** * @@ -121,8 +143,18 @@ void prepare(BackProcessingState state) throws ProcessingException { } @Override - public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) throws ProcessingException { + public void processPolygon(VdypPolygon polygon, BackExecutionStep lastStepInclusive) throws ProcessingException { // TODO Auto-generated method stub } + + @Override + protected BackExecutionStep getFirstStep() { + return BackExecutionStep.NONE; + } + + @Override + protected BackExecutionStep getLastStep() { + return BackExecutionStep.ALL; + } } diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java index d1693c1d4..8527dee2f 100644 --- a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -74,17 +74,18 @@ public BackProcessingState primaryOnlyWithSingleSpecies() throws ProcessingExcep final float dq = 31.5006275f; final float tph = BaseAreaTreeDensityDiameter.treesPerHectare(ba, dq); lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.baseArea(ba); sb.loreyHeight(hl); sb.quadMeanDiameter(dq); sb.treesPerHectare(tph); }); - lb.baseAreaByUtilization(ba); - lb.loreyHeightByUtilization(hl); - lb.quadraticMeanDiameterByUtilization(dq); - lb.treesPerHectareByUtilization(tph); + lb.baseArea(ba); + lb.loreyHeight(hl); + lb.quadMeanDiameter(dq); + lb.treesPerHectare(tph); }); }); @@ -139,10 +140,11 @@ public BackProcessingState primaryAndVeteran() throws ProcessingException { }); pb.addLayer(lb -> { lb.layerType(LayerType.VETERAN); - lb.baseAreaByUtilization(20f); + lb.baseArea(20f); lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.baseArea(20f); }); }); @@ -184,9 +186,10 @@ void testSetBavIfVeteranPresentAndTotalIsWrong() throws ProcessingException { }); pb.addLayer(lb -> { lb.layerType(LayerType.VETERAN); - lb.baseAreaByUtilization(42f); // Not the sum of the base areas of the species + lb.baseArea(42f); // Not the sum of the base areas of the species lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.baseArea(20f); }); }); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java index c357bfc6e..8a4e5184e 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java @@ -1,8 +1,9 @@ package ca.bc.gov.nrs.vdyp.application; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -public abstract class ProcessingEngine { +public abstract class ProcessingEngine> { /** * Run all steps of the engine on the given polygon up to and including the given lastStep. @@ -12,77 +13,48 @@ public abstract class ProcessingEngine { * * @throws ProcessingException should an error with the data occur during processing */ - public abstract void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) - throws ProcessingException; - - public enum ExecutionStep { - // Must be first - NONE, // - - CHECK_FOR_WORK, // - CALCULATE_MISSING_SITE_CURVES, // - CALCULATE_COVERAGES, // - DETERMINE_POLYGON_RANKINGS, // - ESTIMATE_MISSING_SITE_INDICES, // - ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES, // - CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX, // - SET_COMPATIBILITY_VARIABLES, // - GROW_1_LAYER_DHDELTA, // - GROW_2_LAYER_BADELTA, // - GROW_3_LAYER_DQDELTA, // - GROW_4_LAYER_BA_AND_DQTPH_EST, // - GROW_5A_LH_EST, // - GROW_5_SPECIES_BADQTPH, // - GROW_6_LAYER_TPH2, // - GROW_7_LAYER_DQ2, // - GROW_8_SPECIES_LH, // - GROW_9_SPECIES_PCT, // - GROW_10_COMPATIBILITY_VARS, // - GROW_11_SPECIES_UC, // - GROW_12_SPECIES_UC_SMALL, // - GROW_13_STORE_SPECIES_DETAILS, // - GROW, // - - // Must be last - ALL; // - - public ExecutionStep predecessor() { - if (this == NONE) { - throw new IllegalStateException("ExecutionStep.None has no predecessor"); - } - - return ExecutionStep.values()[ordinal() - 1]; - } + public abstract void processPolygon(VdypPolygon polygon, E lastStepInclusive) throws ProcessingException; - public ExecutionStep successor() { - if (this == ALL) { - throw new IllegalStateException("ExecutionStep.All has no successor"); - } + public static interface ExecutionStep> extends Comparable { - return ExecutionStep.values()[ordinal() + 1]; - } + /** + * @return The previous execution step + * @throws IllegalStateException if this is the first step + */ + public E predecessor() throws IllegalStateException; + + /** + * @return The next execution step + * @throws IllegalStateException if this is the last step + */ + public E successor() throws IllegalStateException; - public boolean lt(ExecutionStep that) { - return this.ordinal() < that.ordinal(); + public default boolean lt(E that) { + return this.compareTo(that) < 0; } - public boolean le(ExecutionStep that) { - return this.ordinal() <= that.ordinal(); + public default boolean le(E that) { + return this.compareTo(that) <= 0; } - public boolean eq(ExecutionStep that) { - return this.ordinal() == that.ordinal(); + public default boolean eq(E that) { + return this.compareTo(that) == 0; } - public boolean ge(ExecutionStep that) { - return this.ordinal() >= that.ordinal(); + public default boolean ge(E that) { + return this.compareTo(that) >= 0; } - public boolean gt(ExecutionStep that) { - return this.ordinal() > that.ordinal(); + public default boolean gt(E that) { + return this.compareTo(that) > 0; + } } + protected abstract E getFirstStep(); + + protected abstract E getLastStep(); + /** * Run all steps of the engine on the given polygon. * @@ -92,7 +64,7 @@ public boolean gt(ExecutionStep that) { */ public void processPolygon(VdypPolygon polygon) throws ProcessingException { - processPolygon(polygon, ExecutionStep.ALL); + processPolygon(polygon, getLastStep()); } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java index 720e93aa9..7994e9a78 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.common; +import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -534,4 +535,69 @@ public static float[] specFraction(VdypLayer layer) { public static double computeInFeet(double value, DoubleUnaryOperator operation) { return operation.applyAsDouble(value / METRES_PER_FOOT) * METRES_PER_FOOT; } + + /** + * Given a particular Enum value, and an ordered array of all values of that enum could take on (the result of + * calling values()) Gives the next Enum. + * + * @param + * @param current + * @param values + * @return + * @throws IllegalStateException If the given Enum value is the last one + */ + public static > T successorOrThrow(T current, T[] values) { + return successor(current, values) + .orElseThrow(() -> new IllegalStateException(MessageFormat.format("{} has no successor", current))); + } + + /** + * Given a particular Enum value, and an ordered array of all values of that enum could take on (the result of + * calling values()) Gives the precious Enum. + * + * @param + * @param current + * @param values + * @return + * @throws IllegalStateException If the given Enum value is the first one + */ + public static > T predecessorOrThrow(T current, T[] values) { + return successor(current, values) + .orElseThrow(() -> new IllegalStateException(MessageFormat.format("{} has no predecessor", current))); + } + + /** + * Given a particular Enum value, and an ordered array of all values of that enum could take on (the result of + * calling values()) Gives the next Enum. + * + * @param + * @param current + * @param values + * @return + */ + public static > Optional successor(T current, T[] values) { + int i = current.ordinal(); + if (i <= 0) { + return Optional.empty(); + } + return Optional.of(values[i + 1]); + } + + /** + * Given a particular Enum value, and an ordered array of all values of that enum could take on (the result of + * calling values()) Gives the next Enum. + * + * @param + * @param current + * @param values + * @return + * @throws IllegalStateException If the given Enum value is the first one + */ + public static > Optional predecessor(T current, T[] values) { + int i = current.ordinal(); + if (i <= 0) { + return Optional.empty(); + } + return Optional.of(values[i - 1]); + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java index 6e278577e..7012fbf31 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/model/VdypSpeciesParser.java @@ -160,7 +160,8 @@ protected ValueOrMarker, EndOfRecord> convert(Map { @@ -169,7 +170,7 @@ protected ValueOrMarker, EndOfRecord> convert(Map, I extends BaseVdypSite> { @@ -114,7 +116,7 @@ public int hashCode() { } public abstract static class Builder, S extends BaseVdypSpecies, I extends BaseVdypSite, SB extends BaseVdypSpecies.Builder, IB extends BaseVdypSite.Builder> - extends ModelClassBuilder { + extends ModelClassBuilder implements LayerIdentifiedBuilder { protected Optional polygonIdentifier = Optional.empty(); protected Optional layerType = Optional.empty(); @@ -124,24 +126,14 @@ public abstract static class Builder, S extends Ba protected List> speciesBuilders = new LinkedList<>(); protected List> siteBuilders = new LinkedList<>(); - public Builder polygonIdentifier(PolygonIdentifier polygonIdentifier) { + @Override + public void polygonIdentifier(PolygonIdentifier polygonIdentifier) { this.polygonIdentifier = Optional.of(polygonIdentifier); - return this; - } - - public Builder polygonIdentifier(String polygonIdentifier) { - this.polygonIdentifier = Optional.of(PolygonIdentifier.split(polygonIdentifier)); - return this; } - public Builder polygonIdentifier(String base, int year) { - this.polygonIdentifier = Optional.of(new PolygonIdentifier(base, year)); - return this; - } - - public Builder layerType(LayerType layer) { + @Override + public void layerType(LayerType layer) { this.layerType = Optional.of(layer); - return this; } public Optional getLayerType() { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java index 82623075f..36183a67a 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java @@ -13,6 +13,9 @@ import java.util.function.Function; import java.util.stream.Collectors; +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; +import ca.bc.gov.nrs.vdyp.model.builders.PolygonIdentifiedBuilder; + public abstract class BaseVdypPolygon, PA, SP extends BaseVdypSpecies, SI extends BaseVdypSite> { private PolygonIdentifier polygonIdentifier; // FIP_P/POLYDESC @@ -145,7 +148,7 @@ protected abstract static class Builder< // SPB extends BaseVdypSpecies.Builder, // SIB extends BaseVdypSite.Builder> // - extends ModelClassBuilder { + extends ModelClassBuilder implements PolygonIdentifiedBuilder { protected Optional polygonIdentifier = Optional.empty(); protected Optional percentAvailable = Optional.empty(); protected Optional biogeoclimaticZone = Optional.empty(); @@ -156,19 +159,9 @@ protected abstract static class Builder< // protected List layers = new LinkedList<>(); protected List> layersBuilders = new LinkedList<>(); - public Builder polygonIdentifier(PolygonIdentifier polygonIdentifier) { + @Override + public void polygonIdentifier(PolygonIdentifier polygonIdentifier) { this.polygonIdentifier = Optional.of(polygonIdentifier); - return this; - } - - public Builder polygonIdentifier(String polygonIdentifier) { - this.polygonIdentifier = Optional.of(PolygonIdentifier.split(polygonIdentifier)); - return this; - } - - public Builder polygonIdentifier(String base, int year) { - this.polygonIdentifier = Optional.of(new PolygonIdentifier(base, year)); - return this; } public Builder percentAvailable(PA pa) { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSite.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSite.java index d32ba6e87..09b6298cb 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSite.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSite.java @@ -6,6 +6,8 @@ import ca.bc.gov.nrs.vdyp.common.Computed; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; +import ca.bc.gov.nrs.vdyp.model.builders.SpeciesGroupIdentifiedBuilder; public abstract class BaseVdypSite { @@ -93,7 +95,8 @@ public int hashCode() { return (polygonIdentifier.hashCode() * 17 + layerType.hashCode()) * 17 + siteGenus.hashCode(); } - public abstract static class Builder extends ModelClassBuilder { + public abstract static class Builder extends ModelClassBuilder + implements SpeciesGroupIdentifiedBuilder { protected Optional polygonIdentifier = Optional.empty(); protected Optional layerType = Optional.empty(); protected Optional siteGenus = Optional.empty(); @@ -105,29 +108,19 @@ public abstract static class Builder extends ModelClassB protected Optional height = Optional.empty(); protected Optional yearsToBreastHeight = Optional.empty(); - public Builder polygonIdentifier(PolygonIdentifier polygonIdentifier) { + @Override + public void polygonIdentifier(PolygonIdentifier polygonIdentifier) { this.polygonIdentifier = Optional.of(polygonIdentifier); - return this; - } - - public Builder polygonIdentifier(String polygonIdentifier) { - this.polygonIdentifier = Optional.of(PolygonIdentifier.split(polygonIdentifier)); - return this; - } - - public Builder polygonIdentifier(String base, int year) { - this.polygonIdentifier = Optional.of(new PolygonIdentifier(base, year)); - return this; } - public Builder layerType(LayerType layerType) { + @Override + public void layerType(LayerType layerType) { this.layerType = Optional.of(layerType); - return this; } - public Builder siteGenus(String siteGenus) { + @Override + public void genus(String siteGenus) { this.siteGenus = Optional.of(siteGenus); - return this; } public Builder siteIndex(float siteIndex) { @@ -148,7 +141,7 @@ public Builder siteCurveNumber(Optional siteCurveNumber) { return this; } - public Builder siteGenus(Optional siteGenus) { + public Builder genus(Optional siteGenus) { this.siteGenus = siteGenus; return this; } @@ -188,7 +181,7 @@ public Builder adapt(BaseVdypSite source) { height(source.getHeight()); siteIndex(source.getSiteIndex()); siteCurveNumber(source.getSiteCurveNumber()); - siteGenus(source.getSiteGenus()); + genus(source.getSiteGenus()); return this; } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java index 57df2b904..40380e11d 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java @@ -11,9 +11,13 @@ import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; import ca.bc.gov.nrs.vdyp.common.Computed; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.io.parse.coe.GenusDefinitionParser; +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; +import ca.bc.gov.nrs.vdyp.model.builders.SpeciesGroupIdentifiedBuilder; public abstract class BaseVdypSpecies { + private final PolygonIdentifier polygonIdentifier; // FIP_P/POLYDESC // This is also represents the distinction between data stored in @@ -156,7 +160,7 @@ public Optional getSite() { } public abstract static class Builder, I extends BaseVdypSite, IB extends BaseVdypSite.Builder> - extends ModelClassBuilder { + extends ModelClassBuilder implements SpeciesGroupIdentifiedBuilder { protected Optional polygonIdentifier = Optional.empty(); protected Optional layerType = Optional.empty(); protected Optional genus = Optional.empty(); @@ -166,53 +170,29 @@ public abstract static class Builder, I extends Bas protected Sp64DistributionSet sp64DistributionSet = new Sp64DistributionSet(); protected Optional> siteBuilder = Optional.empty(); protected Optional site = Optional.empty(); + protected Optional speciesGroupMap = Optional.empty(); - public Builder polygonIdentifier(PolygonIdentifier polygonIdentifier) { + @Override + public void polygonIdentifier(PolygonIdentifier polygonIdentifier) { this.polygonIdentifier = Optional.of(polygonIdentifier); - return this; - } - - public Builder polygonIdentifier(String polygonIdentifier) { - this.polygonIdentifier = Optional.of(PolygonIdentifier.split(polygonIdentifier)); - return this; } - public Builder polygonIdentifier(String base, int year) { - this.polygonIdentifier = Optional.of(new PolygonIdentifier(base, year)); - return this; + @Override + public void layerType(LayerType layer) { + this.layerType = Optional.of(layer); } - public Builder layerType(LayerType layer) { - this.layerType = Optional.of(layer); - return this; + public void controlMap(Map controlMap) { + this.speciesGroupMap = Optional.of(GenusDefinitionParser.getSpecies(controlMap)); } - /** - * Set both the genus, and at the same time calculates genusIndex from the given controlMap. - * - * @param genus the species genus - * @param controlMap the control map defining the configuration - * @return this builder - */ - public Builder genus(String genus, Map controlMap) { + @Override + public void genus(String genus) { this.genus = Optional.of(genus); - this.genusIndex = Optional.of(GenusDefinitionParser.getIndex(genus, controlMap)); - return this; } - /** - * Set both the genus and its index. It is the responsibility of the caller to ensure that the index is correct - * for the given genus. Use of this method is appropriate only when logic dictates the given genusIndex is - * correct or in those unit tests where correctness isn't critical. - * - * @param genus the species genus - * @param genusIndex the index of the genus in the configuration (control map entry 10) - * @return this builder - */ - public Builder genus(String genus, int genusIndex) { - this.genus = Optional.of(genus); - this.genusIndex = Optional.of(genusIndex); - return this; + public void genus(int index) { + this.genusIndex = Optional.of(index); } public Builder percentGenus(float percentGenus) { @@ -283,6 +263,17 @@ protected void postProcess(T result) { @Override protected void preProcess() { super.preProcess(); + + // If we have a map between alias and index, fill in the one from the other if necessary. + speciesGroupMap.ifPresent(map -> { + GenusDefinition def = genus.map(map::getByAlias).or(() -> genusIndex.map(map::getByIndex)).get(); + + genus = Optional.of(genus.orElse(def.getAlias())); + genusIndex = Optional.of(genusIndex.orElse(def.getIndex())); + + }); + + // Build the site if present site = siteBuilder.map(this::buildSite).or(() -> site); } @@ -299,7 +290,8 @@ protected String getBuilderId() { public Builder adapt(BaseVdypSpecies source) { polygonIdentifier(source.getPolygonIdentifier()); layerType(source.getLayerType()); - genus(source.getGenus(), source.getGenusIndex()); + this.genus(source.getGenus()); + this.genus(source.getGenusIndex()); percentGenus(source.getPercentGenus()); fractionGenus(source.getFractionGenus()); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java new file mode 100644 index 000000000..98e81f50f --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java @@ -0,0 +1,159 @@ +package ca.bc.gov.nrs.vdyp.model; + +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; +import ca.bc.gov.nrs.vdyp.model.builders.SpeciesGroupIdentifiedBuilder; + +public class VdypCompatibilityVariables { + + private final MatrixMap3 cvVolume; + private final MatrixMap2 cvBasalArea; + private final MatrixMap2 cvQuadraticMeanDiameter; + private final Map cvPrimaryLayerSmall; + + public VdypCompatibilityVariables( + MatrixMap3 cvVolume, + MatrixMap2 cvBasalArea, + MatrixMap2 cvQuadraticMeanDiameter, + Map cvPrimaryLayerSmall + ) { + super(); + this.cvVolume = cvVolume; + this.cvBasalArea = cvBasalArea; + this.cvQuadraticMeanDiameter = cvQuadraticMeanDiameter; + this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; + } + + public MatrixMap3 getCvVolume() { + return cvVolume; + } + + public MatrixMap2 getCvBasalArea() { + return cvBasalArea; + } + + public MatrixMap2 getCvQuadraticMeanDiameter() { + return cvQuadraticMeanDiameter; + } + + public Map getCvPrimaryLayerSmall() { + return cvPrimaryLayerSmall; + } + + /** + * Accepts a configuration function that accepts a builder to configure. + * + *
+	 * FipSpecies myLayer = FipSpecies.build(builder-> {
+			builder.polygonIdentifier(polygonId);
+			builder.layerType(LayerType.VETERAN);
+			builder.genus("B");
+			builder.percentGenus(6f);
+	 * })
+	 * 
+ * + * @param config The configuration function + * @return The object built by the configured builder. + * @throws IllegalStateException if any required properties have not been set by the configuration function. + */ + public static VdypCompatibilityVariables build(Consumer config) { + var builder = new Builder(); + config.accept(builder); + return builder.build(); + } + + public static VdypCompatibilityVariables build(VdypSpecies spec, Consumer config) { + var builder = new Builder(); + builder.polygonIdentifier(spec.getPolygonIdentifier()); + builder.layerType(spec.getLayerType()); + builder.genus(spec.getGenus()); + config.accept(builder); + return builder.build(); + } + + public static class Builder extends ModelClassBuilder + implements SpeciesGroupIdentifiedBuilder { + + private Optional polygonIdentifier = Optional.empty(); + private Optional layerType = Optional.empty(); + private Optional speciesGroup = Optional.empty(); + + public void polygonIndentifier(PolygonIdentifier polyId) { + this.polygonIndentifier(polyId); + } + + private Optional> cvVolume = Optional.empty(); + private Optional> cvBasalArea = Optional.empty(); + private Optional> cvQuadraticMeanDiameter = Optional.empty(); + private Optional> cvPrimaryLayerSmall = Optional.empty(); + + public void cvVolume(MatrixMap3 cvVolume) { + this.cvVolume = Optional.of(cvVolume); + } + + public void cvBasalArea(MatrixMap2 cvBasalArea) { + this.cvBasalArea = Optional.of(cvBasalArea); + } + + public void cvQuadraticMeanDiameter(MatrixMap2 cvQuadraticMeanDiameter) { + this.cvQuadraticMeanDiameter = Optional.of(cvQuadraticMeanDiameter); + } + + public void cvPrimaryLayerSmall(Map cvPrimaryLayerSmall) { + this.cvPrimaryLayerSmall = Optional.of(cvPrimaryLayerSmall); + } + + @Override + protected void check(Collection errors) { + requirePresent(cvVolume, "cvVolume", errors); + requirePresent(cvBasalArea, "cvBasalArea", errors); + requirePresent(cvQuadraticMeanDiameter, "cvQuadraticMeanDiameter", errors); + requirePresent(cvPrimaryLayerSmall, "cvPrimaryLayerSmall", errors); + } + + @Override + protected String getBuilderId() { + return MessageFormat.format( + "Compatibility Variables {0} {1} {2}", // + polygonIdentifier.map(Object::toString).orElse("N/A"), // + layerType.map(Object::toString).orElse("N/A"), // + speciesGroup.map(Object::toString).orElse("N/A")// + ); + } + + public void copy(VdypCompatibilityVariables toCopy) { + cvVolume(toCopy.getCvVolume()); + cvBasalArea(toCopy.getCvBasalArea()); + cvQuadraticMeanDiameter(toCopy.getCvQuadraticMeanDiameter()); + cvPrimaryLayerSmall(toCopy.getCvPrimaryLayerSmall()); + } + + @Override + protected VdypCompatibilityVariables doBuild() { + return new VdypCompatibilityVariables( + cvVolume.get(), cvBasalArea.get(), cvQuadraticMeanDiameter.get(), cvPrimaryLayerSmall.get() + ); + } + + @Override + public void layerType(LayerType type) { + this.layerType = Optional.of(type); + } + + @Override + public void polygonIdentifier(PolygonIdentifier polygonIdentifier) { + this.polygonIdentifier = Optional.of(polygonIdentifier); + } + + @Override + public void genus(String speciesGroup) { + this.speciesGroup = Optional.of(speciesGroup); + } + + } +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java index 518df5568..19947d12e 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java @@ -210,7 +210,8 @@ public static VdypLayer build(VdypPolygon polygon, Consumer config) { } public static class Builder - extends BaseVdypLayer.Builder { + extends BaseVdypLayer.Builder + implements VdypUtilizationHolder.Builder { Optional empiricalRelationshipParameterIndex = Optional.empty(); @@ -235,101 +236,68 @@ public VdypLayer.Builder primaryGenus(String primarySp0) { UtilizationVector loreyHeightByUtilization = VdypUtilizationHolder.emptyLoreyHeightUtilization(); - public void loreyHeightByUtilization(float height) { - this.loreyHeightByUtilization = Utils.heightVector(0, height); - } - - public void loreyHeightByUtilization(float small, float height) { - this.loreyHeightByUtilization = Utils.heightVector(small, height); + @Override + public void loreyHeight(UtilizationVector vector) { + this.loreyHeightByUtilization = vector; } protected UtilizationVector baseAreaByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void baseAreaByUtilization(float small, float u1, float u2, float u3, float u4) { - this.baseAreaByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void baseAreaByUtilization(float height) { - this.baseAreaByUtilization = Utils.utilizationVector(height); + @Override + public void baseArea(UtilizationVector vector) { + this.baseAreaByUtilization = vector; } protected UtilizationVector treesPerHectareByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void treesPerHectareByUtilization(float small, float u1, float u2, float u3, float u4) { - this.treesPerHectareByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void treesPerHectareByUtilization(float height) { - this.treesPerHectareByUtilization = Utils.utilizationVector(height); + @Override + public void treesPerHectare(UtilizationVector vector) { + this.treesPerHectareByUtilization = vector; } protected UtilizationVector quadraticMeanDiameterByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void - quadraticMeanDiameterByUtilization(float small, float uAll, float u1, float u2, float u3, float u4) { - this.quadraticMeanDiameterByUtilization = Utils.utilizationVector(small, uAll, u1, u2, u3, u4); - } - - public void quadraticMeanDiameterByUtilization(float height) { - this.quadraticMeanDiameterByUtilization = Utils.utilizationVector(height); + @Override + public void quadMeanDiameter(UtilizationVector vector) { + this.quadraticMeanDiameterByUtilization = vector; } protected UtilizationVector wholeStemVolumeByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void wholeStemVolumeByUtilization(float small, float u1, float u2, float u3, float u4) { - this.wholeStemVolumeByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void wholeStemVolumeByUtilization(float volume) { - this.wholeStemVolumeByUtilization = Utils.utilizationVector(volume); + @Override + public void wholeStemVolume(UtilizationVector vector) { + this.wholeStemVolumeByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void closeUtilizationVolumeByUtilization(float small, float u1, float u2, float u3, float u4) { - this.closeUtilizationVolumeByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeByUtilization(float volume) { - this.closeUtilizationVolumeByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeNetOfDecayByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayByUtilization(float small, float u1, float u2, float u3, float u4) { - this.closeUtilizationVolumeNetOfDecayByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayByUtilization(float volume) { - this.closeUtilizationVolumeNetOfDecayByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeNetOfDecayByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeNetOfDecayAndWasteByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization( - float small, float u1, float u2, float u3, float u4 - ) { - this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(float volume) { - this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - float small, float u1, float u2, float u3, float u4 - ) { - this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = Utils - .utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(float volume) { - this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = vector; } @Override diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java index e59d9468d..8928be05f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java @@ -104,6 +104,10 @@ protected VdypLayer.Builder getLayerBuilder() { public void targetYear(Optional targetYear) { this.targetYear = targetYear; } + + public void targetYear(int targetYear) { + targetYear(Optional.of(targetYear)); + } } public void setTargetYear(int year) { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java index d2f680e9b..21aaf2f90 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java @@ -29,10 +29,7 @@ public class VdypSpecies extends BaseVdypSpecies implements VdypUtiliz // Compatibility Variables - private Optional> cvVolume = Optional.empty(); - private Optional> cvBasalArea = Optional.empty(); - private Optional> cvQuadraticMeanDiameter = Optional.empty(); - private Optional> cvPrimaryLayerSmall = Optional.empty(); + private Optional compatibilityVariables = Optional.empty(); public VdypSpecies( PolygonIdentifier polygonIdentifier, LayerType layer, String genus, int genusIndex, @@ -204,40 +201,38 @@ public void setCompatibilityVariables( Map cvPrimaryLayerSmall ) { - this.cvVolume = Optional.of(cvVolume); - this.cvBasalArea = Optional.of(cvBasalArea); - this.cvQuadraticMeanDiameter = Optional.of(cvQuadraticMeanDiameter); - this.cvPrimaryLayerSmall = Optional.of(cvPrimaryLayerSmall); + this.compatibilityVariables = Optional.of(VdypCompatibilityVariables.build(this, cvb -> { + + cvb.cvVolume(cvVolume); + cvb.cvBasalArea(cvBasalArea); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); + + })); } public float getCvVolume(UtilizationClass uc, VolumeVariable vv, LayerType lt) { - if (cvVolume.isEmpty()) { - throw new InitializationIncompleteException(MessageFormat.format("Species {0}: cvVolume", this)); - } - return cvVolume.get().get(uc, vv, lt); + return requireCompatibilityVariables().getCvVolume().get(uc, vv, lt); } public float getCvBasalArea(UtilizationClass uc, LayerType lt) { - if (cvBasalArea.isEmpty()) { - throw new InitializationIncompleteException(MessageFormat.format("Species {0}: cvBasalArea", this)); - } - return cvBasalArea.get().get(uc, lt); + return requireCompatibilityVariables().getCvBasalArea().get(uc, lt); } public float getCvQuadraticMeanDiameter(UtilizationClass uc, LayerType lt) { - if (cvQuadraticMeanDiameter.isEmpty()) { - throw new InitializationIncompleteException( - MessageFormat.format("Species {0}: cvQuadraticMeanDiameter", this) - ); - } - return cvQuadraticMeanDiameter.get().get(uc, lt); + return requireCompatibilityVariables().getCvQuadraticMeanDiameter().get(uc, lt); } public float getCvPrimaryLayerSmall(UtilizationClassVariable ucv) { - if (cvPrimaryLayerSmall.isEmpty()) { - throw new InitializationIncompleteException(MessageFormat.format("Species {0}: cvPrimaryLayerSmall", this)); - } - return cvPrimaryLayerSmall.get().get(ucv); + return requireCompatibilityVariables().getCvPrimaryLayerSmall().get(ucv); + } + + protected VdypCompatibilityVariables requireCompatibilityVariables() { + return this.compatibilityVariables.orElseThrow( + () -> new InitializationIncompleteException( + MessageFormat.format("Species {0}: compatibilityVariables", this) + ) + ); } /** @@ -280,107 +275,76 @@ public static VdypSpecies build(VdypLayer layer, Consumer config) { return result; } - public static class Builder extends BaseVdypSpecies.Builder { + public static class Builder extends BaseVdypSpecies.Builder + implements VdypUtilizationHolder.Builder { protected Optional volumeGroup = Optional.empty(); protected Optional decayGroup = Optional.empty(); protected Optional breakageGroup = Optional.empty(); protected UtilizationVector loreyHeight = VdypUtilizationHolder.emptyLoreyHeightUtilization(); - public void loreyHeight(float height) { - this.loreyHeight = Utils.heightVector(0, height); - } - - public void loreyHeight(float small, float height) { - this.loreyHeight = Utils.heightVector(small, height); + @Override + public void loreyHeight(UtilizationVector vector) { + this.loreyHeight = vector; } protected UtilizationVector baseArea = VdypUtilizationHolder.emptyUtilization(); - public void baseArea(float small, float u1, float u2, float u3, float u4) { - this.baseArea = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void baseArea(float baseArea) { - this.baseArea = Utils.utilizationVector(baseArea); + @Override + public void baseArea(UtilizationVector vector) { + this.baseArea = vector; } protected UtilizationVector treesPerHectare = VdypUtilizationHolder.emptyUtilization(); - public void treesPerHectare(float small, float u1, float u2, float u3, float u4) { - this.treesPerHectare = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void treesPerHectare(float height) { - this.treesPerHectare = Utils.utilizationVector(height); + @Override + public void treesPerHectare(UtilizationVector vector) { + this.treesPerHectare = vector; } protected UtilizationVector quadMeanDiameter = VdypUtilizationHolder.emptyUtilization(); - public void quadMeanDiameter(float small, float uAll, float u1, float u2, float u3, float u4) { - this.quadMeanDiameter = Utils.utilizationVector(small, uAll, u1, u2, u3, u4); - } - - public void quadMeanDiameter(float height) { - this.quadMeanDiameter = Utils.utilizationVector(height); + @Override + public void quadMeanDiameter(UtilizationVector vector) { + this.quadMeanDiameter = vector; } protected UtilizationVector wholeStemVolume = VdypUtilizationHolder.emptyUtilization(); - public void wholeStemVolume(float small, float u1, float u2, float u3, float u4) { - this.wholeStemVolume = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void wholeStemVolume(float volume) { - this.wholeStemVolume = Utils.utilizationVector(volume); + @Override + public void wholeStemVolume(UtilizationVector vector) { + this.wholeStemVolume = vector; } protected UtilizationVector closeUtilizationVolumeByUtilization = VdypUtilizationHolder.emptyUtilization(); - public void closeUtilizationVolumeByUtilization(float small, float u1, float u2, float u3, float u4) { - this.closeUtilizationVolumeByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeByUtilization(float volume) { - this.closeUtilizationVolumeByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeByUtilization = vector; } protected UtilizationVector closeUtilizationNetVolumeOfDecayByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayByUtilization(float small, float u1, float u2, float u3, float u4) { - this.closeUtilizationNetVolumeOfDecayByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayByUtilization(float volume) { - this.closeUtilizationNetVolumeOfDecayByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayByUtilization(UtilizationVector vector) { + this.closeUtilizationNetVolumeOfDecayByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeNetOfDecayAndWasteByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization( - float small, float u1, float u2, float u3, float u4 - ) { - this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = Utils.utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(float volume) { - this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization = vector; } protected UtilizationVector closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = VdypUtilizationHolder .emptyUtilization(); - public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - float small, float u1, float u2, float u3, float u4 - ) { - this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = Utils - .utilizationVector(small, u1, u2, u3, u4); - } - - public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(float volume) { - this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = Utils.utilizationVector(volume); + @Override + public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(UtilizationVector vector) { + this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = vector; } @Override @@ -467,7 +431,7 @@ protected VdypSite buildSite(Consumer config) { config.accept(builder); builder.polygonIdentifier(polygonIdentifier.get()); builder.layerType(layerType.get()); - builder.siteGenus(genus); + builder.genus(genus); }); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypUtilizationHolder.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypUtilizationHolder.java index a9d219518..6956aafc4 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypUtilizationHolder.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypUtilizationHolder.java @@ -1,5 +1,7 @@ package ca.bc.gov.nrs.vdyp.model; +import ca.bc.gov.nrs.vdyp.common.Utils; + /** * Common accessors for utilization vectors shared by Layer and Species * @@ -110,4 +112,107 @@ static UtilizationVector emptyUtilization() { static UtilizationVector emptyLoreyHeightUtilization() { return new UtilizationVector(0f, 0f); } + + static interface Builder { + + public void loreyHeight(UtilizationVector vector); + + public void baseArea(UtilizationVector vector); + + public void treesPerHectare(UtilizationVector utilizationVector); + + public void quadMeanDiameter(UtilizationVector utilizationVector); + + public void wholeStemVolume(UtilizationVector utilizationVector); + + public void closeUtilizationVolumeByUtilization(UtilizationVector utilizationVector); + + public void closeUtilizationVolumeNetOfDecayByUtilization(UtilizationVector utilizationVector); + + public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(UtilizationVector utilizationVector); + + public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(UtilizationVector utilizationVector); + + // The following methods are syntactic sugar for the preceding + + public default void loreyHeight(float height) { + this.loreyHeight(Utils.heightVector(0, height)); + } + + public default void loreyHeight(float small, float height) { + this.loreyHeight(Utils.heightVector(small, height)); + } + + public default void baseArea(float small, float u1, float u2, float u3, float u4) { + this.baseArea(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void baseArea(float baseArea) { + this.baseArea(Utils.utilizationVector(baseArea)); + } + + public default void treesPerHectare(float small, float u1, float u2, float u3, float u4) { + this.treesPerHectare(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void treesPerHectare(float height) { + this.treesPerHectare(Utils.utilizationVector(height)); + } + + public default void quadMeanDiameter(float small, float uAll, float u1, float u2, float u3, float u4) { + this.quadMeanDiameter(Utils.utilizationVector(small, uAll, u1, u2, u3, u4)); + } + + public default void quadMeanDiameter(float height) { + this.quadMeanDiameter(Utils.utilizationVector(height)); + } + + public default void wholeStemVolume(float small, float u1, float u2, float u3, float u4) { + this.wholeStemVolume(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void wholeStemVolume(float volume) { + this.wholeStemVolume(Utils.utilizationVector(volume)); + } + + public default void closeUtilizationVolumeByUtilization(float small, float u1, float u2, float u3, float u4) { + this.closeUtilizationVolumeByUtilization(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void closeUtilizationVolumeByUtilization(float volume) { + this.closeUtilizationVolumeByUtilization(Utils.utilizationVector(volume)); + } + + public default void + closeUtilizationVolumeNetOfDecayByUtilization(float small, float u1, float u2, float u3, float u4) { + this.closeUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void closeUtilizationVolumeNetOfDecayByUtilization(float volume) { + this.closeUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(volume)); + } + + public default void closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + float small, float u1, float u2, float u3, float u4 + ) { + this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(small, u1, u2, u3, u4)); + } + + public default void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(float volume) { + this.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(volume)); + } + + public default void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + float small, float u1, float u2, float u3, float u4 + ) { + this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(small, u1, u2, u3, u4) + ); + } + + public default void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(float volume) { + this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(Utils.utilizationVector(volume)); + } + + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/LayerIdentifiedBuilder.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/LayerIdentifiedBuilder.java new file mode 100644 index 000000000..47507d104 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/LayerIdentifiedBuilder.java @@ -0,0 +1,11 @@ +package ca.bc.gov.nrs.vdyp.model.builders; + +import ca.bc.gov.nrs.vdyp.model.LayerType; + +/** + * Interface for a builder for a model object keyed by a layer + */ +public interface LayerIdentifiedBuilder extends PolygonIdentifiedBuilder { + + void layerType(LayerType type); +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/ModelClassBuilder.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/ModelClassBuilder.java similarity index 97% rename from lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/ModelClassBuilder.java rename to lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/ModelClassBuilder.java index bd41f903d..9b0f02813 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/ModelClassBuilder.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/ModelClassBuilder.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.model; +package ca.bc.gov.nrs.vdyp.model.builders; import java.text.MessageFormat; import java.util.Collection; diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/PolygonIdentifiedBuilder.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/PolygonIdentifiedBuilder.java new file mode 100644 index 000000000..08af5bd4c --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/PolygonIdentifiedBuilder.java @@ -0,0 +1,18 @@ +package ca.bc.gov.nrs.vdyp.model.builders; + +import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; + +/** + * Interface for a builder for a model object keyed by a polygon + */ +public interface PolygonIdentifiedBuilder { + void polygonIdentifier(PolygonIdentifier polygonIdentifier); + + default void polygonIdentifier(String string) { + polygonIdentifier(PolygonIdentifier.split(string)); + } + + default void polygonIdentifier(String base, int year) { + polygonIdentifier(new PolygonIdentifier(base, year)); + } +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/SpeciesGroupIdentifiedBuilder.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/SpeciesGroupIdentifiedBuilder.java new file mode 100644 index 000000000..4368f897e --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/builders/SpeciesGroupIdentifiedBuilder.java @@ -0,0 +1,8 @@ +package ca.bc.gov.nrs.vdyp.model.builders; + +/** + * Interface for a builder for a model object keyed by a species group + */ +public interface SpeciesGroupIdentifiedBuilder extends LayerIdentifiedBuilder { + void genus(String speciesGroup); +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java index 781a7a70f..277d181d1 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/Bank.java @@ -385,7 +385,7 @@ private VdypSpecies transferSpeciesFromBank(int index, VdypSpecies species) { speciesBuilder.percentGenus(this.percentagesOfForestedLand[index]); species.getSite().ifPresentOrElse(site -> speciesBuilder.addSite(VdypSite.build(siteBuilder -> { siteBuilder.copy(site); - siteBuilder.siteGenus(this.speciesNames[index]); + siteBuilder.genus(this.speciesNames[index]); siteBuilder.ageTotal(Utils.optFloat(ageTotals[index])); siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); @@ -395,7 +395,7 @@ private VdypSpecies transferSpeciesFromBank(int index, VdypSpecies species) { VdypSite site = VdypSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(species.getPolygonIdentifier()); siteBuilder.layerType(species.getLayerType()); - siteBuilder.siteGenus(this.speciesNames[index]); + siteBuilder.genus(this.speciesNames[index]); siteBuilder.ageTotal(Utils.optFloat(this.ageTotals[index])); siteBuilder.height(Utils.optFloat(this.dominantHeights[index])); siteBuilder.siteCurveNumber(Utils.optInt(this.siteCurveNumbers[index])); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java index c30585e50..da488ae27 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplicationTest.java @@ -757,12 +757,14 @@ void testSimple() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -794,12 +796,14 @@ void testHeightCloseToA2() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -831,12 +835,14 @@ void testLowCrownClosure() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(9f);// Altered this in the debugger while running VDYP7 lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -868,12 +874,14 @@ void testLowResult() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -915,12 +923,14 @@ void testSimple() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -953,12 +963,14 @@ void testHeightLessThanA5() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -991,12 +1003,14 @@ void testResultLargerThanUpperBound() throws Exception { lb.layerType(LayerType.PRIMARY); lb.crownClosure(82.8000031f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(33f); sb.addSp64Distribution("B", 100f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(67f); sb.addSp64Distribution("H", 100f); sb.addSite(ib -> { @@ -1064,10 +1078,11 @@ void testWithoutVeteran() throws Exception { lb.crownClosure(60f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(10); sb.addSite(ib -> { - ib.siteGenus("L"); + ib.genus("L"); ib.ageTotal(60f); ib.height(15f); ib.siteIndex(5f); @@ -1075,7 +1090,8 @@ void testWithoutVeteran() throws Exception { }); }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(90); }); }); @@ -1116,15 +1132,18 @@ public VdypApplicationIdentifier getId() { lb.crownClosure(82.8f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(15); }); lb.addSpecies(sb -> { - sb.genus("D", controlMap); + sb.controlMap(controlMap); + sb.genus("D"); sb.percentGenus(7); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(77); sb.addSite(ib -> { ib.ageTotal(45f); @@ -1135,7 +1154,8 @@ public VdypApplicationIdentifier getId() { }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(1); }); }); @@ -1145,10 +1165,10 @@ public VdypApplicationIdentifier getId() { lb.crownClosure(4f); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(100); sb.addSite(ib -> { - ib.siteGenus("H"); ib.ageTotal(105f); ib.height(26.2f); ib.siteIndex(16.7f); @@ -1193,7 +1213,8 @@ void testPass(String dist, float expected) throws Exception { for (String s : dist.split(" ")) { var parts = s.split(":"); lb.addSpecies(sb -> { - sb.genus(parts[0], controlMap); + sb.controlMap(controlMap); + sb.genus(parts[0]); sb.percentGenus(Float.valueOf(parts[1])); }); } @@ -1225,7 +1246,8 @@ void testFail(String dist) throws Exception { for (String s : dist.split(" ")) { var parts = s.split(":"); lb.addSpecies(sb -> { - sb.genus(parts[0], controlMap); + sb.controlMap(controlMap); + sb.genus(parts[0]); sb.percentGenus(Float.valueOf(parts[1])); }); } @@ -1258,7 +1280,8 @@ void testEstimate() throws ProcessingException, IOException { lb.primaryGenus("H"); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(20f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -1271,7 +1294,8 @@ void testEstimate() throws ProcessingException, IOException { sb.wholeStemVolume(635.659668f); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(20f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -1283,7 +1307,8 @@ void testEstimate() throws ProcessingException, IOException { sb.wholeStemVolume(6.35662031f); }); lb.addSpecies(sb -> { - sb.genus("D", controlMap); + sb.controlMap(controlMap); + sb.genus("D"); sb.percentGenus(20f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -1295,7 +1320,8 @@ void testEstimate() throws ProcessingException, IOException { sb.wholeStemVolume(44.496151f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(20f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -1313,7 +1339,8 @@ void testEstimate() throws ProcessingException, IOException { }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(20f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -1325,11 +1352,11 @@ void testEstimate() throws ProcessingException, IOException { sb.wholeStemVolume(57.2091446f); }); - lb.loreyHeightByUtilization(31.3307209f); - lb.baseAreaByUtilization(44.6249847f); - lb.treesPerHectareByUtilization(620.484802f); - lb.quadraticMeanDiameterByUtilization(30.2606697f); - lb.wholeStemVolumeByUtilization(635.659668f); + lb.loreyHeight(31.3307209f); + lb.baseArea(44.6249847f); + lb.treesPerHectare(620.484802f); + lb.quadMeanDiameter(30.2606697f); + lb.wholeStemVolume(635.659668f); }); app.estimateSmallComponents(fPoly, layer); @@ -1395,7 +1422,8 @@ void testCompute() throws ProcessingException, IOException { lb.inventoryTypeGroup(14); lb.primaryGenus("H"); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(20f); sb.volumeGroup(15); @@ -1410,7 +1438,8 @@ void testCompute() throws ProcessingException, IOException { sb.addSp64Distribution("BL", 100); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(30f); sb.volumeGroup(23); @@ -1425,7 +1454,8 @@ void testCompute() throws ProcessingException, IOException { sb.addSp64Distribution("CW", 100); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(50f); sb.volumeGroup(40); @@ -1535,7 +1565,8 @@ void testApplyToBuilder() throws Exception { var result = VdypSpecies.build(sb -> { sb.polygonIdentifier("Test", 2024); sb.layerType(LayerType.PRIMARY); - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(100); app.applyGroups(bec, "B", sb); }); @@ -1559,7 +1590,8 @@ void testApplyToObject() throws Exception { pb.addLayer(lb -> { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(100); }); }); @@ -1613,7 +1645,8 @@ void testGetCoeForSpecies() throws Exception { var species = TestSpecies.build(sb -> { sb.polygonIdentifier("TestPolygon", 2024); sb.layerType(LayerType.PRIMARY); - sb.genus("MB", controlMap); + sb.controlMap(controlMap); + sb.genus("MB"); sb.percentGenus(90); }); var result = app.getCoeForSpecies(species, ControlKey.SMALL_COMP_BA); @@ -1689,7 +1722,8 @@ void testComputeUtilizationComponentsPrimaryByUtilNoCV() throws ProcessingExcept layer.getWholeStemVolumeByUtilization().setSmall(0.107688069f); var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("L", controlMap); + builder.controlMap(controlMap); + builder.genus("L"); builder.percentGenus(11.0567074f); builder.volumeGroup(46); builder.decayGroup(38); @@ -1709,7 +1743,8 @@ void testComputeUtilizationComponentsPrimaryByUtilNoCV() throws ProcessingExcept spec1.getWholeStemVolumeByUtilization().setSmall(0.0411359742f); var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("PL", controlMap); + builder.controlMap(controlMap); + builder.genus("PL"); builder.percentGenus(88.9432907f); builder.volumeGroup(54); builder.decayGroup(42); @@ -1872,7 +1907,8 @@ TestLayer getTestPrimaryLayer( builder.polygonIdentifier(polygonId); builder.layerType(LayerType.PRIMARY); builder.addSpecies(specBuilder -> { - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.addSite(siteBuilder -> { siteBuilder.ageTotal(8f); siteBuilder.yearsToBreastHeight(7f); @@ -1897,7 +1933,8 @@ TestLayer getTestVeteranLayer( builder.layerType(LayerType.VETERAN); builder.addSpecies(specBuilder -> { - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.addSite(siteBuilder -> { siteBuilder.ageTotal(8f); siteBuilder.yearsToBreastHeight(7f); @@ -1924,7 +1961,8 @@ TestSpecies getTestSpecies( var result = TestSpecies.build(builder -> { builder.polygonIdentifier(polygonId); builder.layerType(layer); - builder.genus(genusId, controlMap); + builder.controlMap(controlMap); + builder.genus(genusId); builder.percentGenus(100.0f); builder.addSp64Distribution(genusId, 100f); }); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestSpecies.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestSpecies.java index 358acdb17..94e64c0e6 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestSpecies.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestSpecies.java @@ -40,7 +40,7 @@ protected TestSite buildSite(Consumer config) { return TestSite.build(builder -> { builder.polygonIdentifier(this.polygonIdentifier.get()); builder.layerType(this.layerType.get()); - builder.siteGenus(this.genus.get()); + builder.genus(this.genus.get()); config.accept(builder); }); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/EstimationMethodsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/EstimationMethodsTest.java index 49122c6c5..77e4f4cfc 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/EstimationMethodsTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/EstimationMethodsTest.java @@ -439,7 +439,8 @@ void testSimple() throws Exception { // sp 3, 4, 5, 8, 15 // sp B, C, D, H, S var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.volumeGroup(12); builder.decayGroup(7); builder.breakageGroup(5); @@ -449,7 +450,8 @@ void testSimple() throws Exception { spec1.setFractionGenus(0.00817133673f); var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("C", controlMap); + builder.controlMap(controlMap); + builder.genus("C"); builder.volumeGroup(4); builder.decayGroup(14); builder.breakageGroup(6); @@ -459,7 +461,8 @@ void testSimple() throws Exception { spec2.setFractionGenus(0.0972022042f); var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("D", controlMap); + builder.controlMap(controlMap); + builder.genus("D"); builder.volumeGroup(25); builder.decayGroup(19); builder.breakageGroup(12); @@ -469,7 +472,8 @@ void testSimple() throws Exception { spec3.setFractionGenus(0.695440531f); var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("H", controlMap); + builder.controlMap(controlMap); + builder.genus("H"); builder.volumeGroup(37); builder.decayGroup(31); builder.breakageGroup(17); @@ -484,7 +488,8 @@ void testSimple() throws Exception { spec4.setFractionGenus(0.117043354f); var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus("S", controlMap); + builder.controlMap(controlMap); + builder.genus("S"); builder.volumeGroup(66); builder.decayGroup(54); builder.breakageGroup(28); @@ -695,7 +700,8 @@ void testEqn1() throws Exception { var spec = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); @@ -704,7 +710,8 @@ void testEqn1() throws Exception { var specPrime = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("H", controlMap); + builder.controlMap(controlMap); + builder.genus("H"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); @@ -727,7 +734,8 @@ void testEqn2() throws Exception { var spec = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); @@ -736,7 +744,8 @@ void testEqn2() throws Exception { var specPrime = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("D", controlMap); + builder.controlMap(controlMap); + builder.genus("D"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/UtilsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/UtilsTest.java index e4a8c7d87..11cc32036 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/UtilsTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common/UtilsTest.java @@ -142,10 +142,11 @@ void testFromSingleValue() { lb.polygonIdentifier("Test", 2024); lb.layerType(LayerType.PRIMARY); - lb.baseAreaByUtilization(0.7f, 0.9f, 1.1f, 1.3f, 1.5f); + lb.baseArea(0.7f, 0.9f, 1.1f, 1.3f, 1.5f); lb.addSpecies(sb -> { - sb.genus("B", 3); + sb.genus("B"); + sb.genus(3); sb.baseArea(0.1f, 0.2f, 0.3f, 0.4f, 0.5f); sb.percentGenus(40); sb.volumeGroup(42); @@ -153,7 +154,8 @@ void testFromSingleValue() { sb.breakageGroup(42); }); lb.addSpecies(sb -> { - sb.genus("C", 4); + sb.genus("C"); + sb.genus(4); sb.baseArea(0.6f, 0.7f, 0.8f, 0.9f, 1f); sb.percentGenus(60); sb.volumeGroup(42); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriterTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriterTest.java index d00a1a36c..6ac598bc9 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriterTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VdypOutputWriterTest.java @@ -103,7 +103,8 @@ void testWritePolygon() throws IOException { builder.layerType(LayerType.PRIMARY); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(1); specBuilder.decayGroup(2); @@ -142,7 +143,8 @@ void testWriteSpecies() throws IOException { builder.primaryGenus("PL"); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(0); specBuilder.decayGroup(0); @@ -186,7 +188,8 @@ void testWriteUtilizationForLayer() throws IOException { builder.layerType(LayerType.PRIMARY); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(1); specBuilder.decayGroup(2); @@ -205,7 +208,8 @@ void testWriteUtilizationForLayer() throws IOException { @SuppressWarnings("unused") var species = VdypSpecies.build(layer, builder -> { - builder.genus("PL", controlMap); + builder.controlMap(controlMap); + builder.genus("PL"); builder.addSp64Distribution("PL", 100f); builder.percentGenus(100f); @@ -273,7 +277,8 @@ void testWriteUtilizationZeroBaseArea() throws IOException { builder.layerType(LayerType.PRIMARY); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(1); specBuilder.decayGroup(2); @@ -290,7 +295,8 @@ void testWriteUtilizationZeroBaseArea() throws IOException { }); var species = VdypSpecies.build(layer, builder -> { - builder.genus("PL", controlMap); + builder.controlMap(controlMap); + builder.genus("PL"); builder.addSp64Distribution("PL", 100f); builder.percentGenus(100f); @@ -363,7 +369,8 @@ void testWritePolygonWithChildren() throws IOException { builder.primaryGenus("PL"); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(0); specBuilder.decayGroup(0); @@ -492,7 +499,8 @@ void testWritePolygonWithChildrenForYear() throws IOException { builder.primaryGenus("PL"); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("PL"); specBuilder.percentGenus(100); specBuilder.volumeGroup(0); specBuilder.decayGroup(0); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java index 0dca090a3..be850a0a1 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java @@ -37,7 +37,8 @@ void build() throws Exception { builder.primaryGenus("PL"); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", 12); + specBuilder.genus("PL"); + specBuilder.genus(12); specBuilder.percentGenus(100); specBuilder.volumeGroup(-1); specBuilder.decayGroup(-1); @@ -86,7 +87,8 @@ void buildForPolygon() throws Exception { builder.primaryGenus("PL"); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", 12); + specBuilder.genus("PL"); + specBuilder.genus(12); specBuilder.percentGenus(100); specBuilder.volumeGroup(-1); specBuilder.decayGroup(-1); @@ -120,7 +122,8 @@ void buildAddSpecies() throws Exception { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); builder.addSpecies(specBuilder -> { - specBuilder.genus("PL", 12); + specBuilder.genus("PL"); + specBuilder.genus(12); specBuilder.percentGenus(100); specBuilder.volumeGroup(-1); specBuilder.decayGroup(-1); @@ -134,7 +137,8 @@ void buildAddSpecies() throws Exception { }); builder.addSpecies(specBuilder -> { - specBuilder.genus("B", 3); + specBuilder.genus("B"); + specBuilder.genus(3); specBuilder.percentGenus(90f); specBuilder.volumeGroup(10); specBuilder.decayGroup(10); @@ -239,7 +243,8 @@ void buildCopySpecies() throws Exception { builder.layerType(LayerType.PRIMARY); builder.addSpecies(speciesBuilder -> { - speciesBuilder.genus("B", 3); + speciesBuilder.genus("B"); + speciesBuilder.genus(3); speciesBuilder.percentGenus(100f); speciesBuilder.fractionGenus(1f); speciesBuilder.volumeGroup(1); @@ -319,11 +324,11 @@ void testAdditionalBuildMethods() { builder.closeUtilizationVolumeNetOfDecayByUtilization(1.0f); builder.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(1.0f); builder.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(1.0f); - builder.wholeStemVolumeByUtilization(1.0f); - builder.baseAreaByUtilization(1.0f); - builder.loreyHeightByUtilization(1.0f, 1.0f); - builder.quadraticMeanDiameterByUtilization(1.0f); - builder.treesPerHectareByUtilization(1.0f); + builder.wholeStemVolume(1.0f); + builder.baseArea(1.0f); + builder.loreyHeight(1.0f, 1.0f); + builder.quadMeanDiameter(1.0f); + builder.treesPerHectare(1.0f); }); assertThat(poly.getLayers(), hasEntry(LayerType.PRIMARY, layer1)); @@ -363,11 +368,11 @@ layer1, hasProperty("closeUtilizationVolumeNetOfDecayByUtilization", is(Utils.ut builder.closeUtilizationVolumeNetOfDecayByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); builder.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); builder.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); - builder.wholeStemVolumeByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); - builder.baseAreaByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); - builder.loreyHeightByUtilization(2.0f, 2.0f); - builder.quadraticMeanDiameterByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f); - builder.treesPerHectareByUtilization(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); + builder.wholeStemVolume(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); + builder.baseArea(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); + builder.loreyHeight(2.0f, 2.0f); + builder.quadMeanDiameter(2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f); + builder.treesPerHectare(2.0f, 2.0f, 2.0f, 2.0f, 2.0f); }); assertThat(poly.getLayers(), hasEntry(LayerType.PRIMARY, layer2)); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSiteTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSiteTest.java index 7d9d4ab31..82de5cfdf 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSiteTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSiteTest.java @@ -22,7 +22,7 @@ void build() throws Exception { builder.ageTotal(35.0f); builder.height(53.0f); builder.siteCurveNumber(22); - builder.siteGenus("B"); + builder.genus("B"); builder.siteIndex(42.5f); builder.yearsToBreastHeight(5.0f); }); @@ -45,7 +45,7 @@ void build() throws Exception { builder.ageTotal(35.0f); builder.height(53.0f); builder.siteCurveNumber(22); - builder.siteGenus("B"); + builder.genus("B"); builder.siteIndex(42.5f); builder.yearsToBreastHeight(5.0f); }); @@ -58,7 +58,7 @@ void build() throws Exception { builder.ageTotal(35.0f); builder.height(53.0f); builder.siteCurveNumber(22); - builder.siteGenus("C"); + builder.genus("C"); builder.siteIndex(42.5f); builder.yearsToBreastHeight(5.0f); }); @@ -71,7 +71,7 @@ void build() throws Exception { builder.ageTotal(35.0f); builder.height(53.0f); builder.siteCurveNumber(22); - builder.siteGenus("B"); + builder.genus("B"); builder.siteIndex(42.5f); builder.yearsToBreastHeight(5.0f); }); @@ -103,7 +103,7 @@ void buildCopy() throws Exception { builder.ageTotal(35.0f); builder.height(53.0f); builder.siteCurveNumber(22); - builder.siteGenus("B"); + builder.genus("B"); builder.siteIndex(42.5f); builder.yearsToBreastHeight(5.0f); }); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java index 83204607f..3ad9364b4 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java @@ -27,7 +27,8 @@ void build() throws Exception { var species1 = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -48,7 +49,8 @@ void build() throws Exception { var species2 = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test2", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -60,7 +62,8 @@ void build() throws Exception { var species3 = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("D", 5); + builder.genus("D"); + builder.genus(5); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -72,7 +75,8 @@ void build() throws Exception { var species4 = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.VETERAN); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -88,7 +92,8 @@ void testAdditionalBuildMethods() { sb.polygonIdentifier(new PolygonIdentifier("Poly1", 2024)); sb.layerType(LayerType.PRIMARY); sb.percentGenus(100.0f); - sb.genus("Species1", 5); + sb.genus("Species1"); + sb.genus(5); sb.baseArea(0.00155f, 0.01412f, 0.05128f, 0.45736f, 28.77972f); sb.treesPerHectare(0.47f, 1.64f, 2.69f, 13.82f, 269.56f); sb.loreyHeight(10.6033f, 33.7440f); @@ -156,7 +161,8 @@ void buildForLayer() throws Exception { var result = VdypSpecies.build(layer, builder -> { builder.polygonIdentifier("Test", 2024); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -186,7 +192,8 @@ void buildAddSpeciesPercent() throws Exception { var result = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); @@ -213,7 +220,8 @@ void adaptSite() throws Exception { var toCopy = VdypSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", 3); + builder.genus("B"); + builder.genus(3); builder.percentGenus(50f); builder.volumeGroup(1); builder.decayGroup(2); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java index df05370dc..345f96d61 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/BankTest.java @@ -49,22 +49,26 @@ void before() throws IOException, ResourceParseException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.baseArea(0.4f); sb.percentGenus(10); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.baseArea(0.6f); sb.percentGenus(10); }); lb.addSpecies(sb -> { - sb.genus("D", controlMap); + sb.controlMap(controlMap); + sb.genus("D"); sb.baseArea(10f); sb.percentGenus(10); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.baseArea(50f); sb.percentGenus(60); sb.addSite(ib -> { @@ -76,7 +80,8 @@ void before() throws IOException, ResourceParseException { }); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.baseArea(99.9f); sb.percentGenus(10); sb.addSite(ib -> { @@ -96,10 +101,10 @@ void before() throws IOException, ResourceParseException { sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(39); }); - lb.quadraticMeanDiameterByUtilization(21); - lb.baseAreaByUtilization(22); - lb.treesPerHectareByUtilization(BaseAreaTreeDensityDiameter.treesPerHectare(22, 21)); - lb.loreyHeightByUtilization(24); + lb.quadMeanDiameter(21); + lb.baseArea(22); + lb.treesPerHectare(BaseAreaTreeDensityDiameter.treesPerHectare(22, 21)); + lb.loreyHeight(24); lb.closeUtilizationVolumeByUtilization(42); lb.closeUtilizationVolumeNetOfDecayByUtilization(41); lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(40); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java index 65dcbb3e8..67b6ab71d 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java @@ -109,7 +109,8 @@ void testConstructOneSpecies() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("A", 1); + sb.genus("A"); + sb.genus(1); }); }); }); @@ -151,7 +152,8 @@ void setup() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("A", 1); + sb.genus("A"); + sb.genus(1); }); }); }); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index d63e90f91..f5bde90e4 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -62,6 +62,7 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.Region; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; public class TestUtils { @@ -638,4 +639,93 @@ public static BiFunction> netDecayMap(i public static BecDefinition mockBec() { return new BecDefinition("T", Region.COASTAL, "Test"); } + + public static void writeToTest(VdypPolygon poly, Appendable out) { + var result = VdypPolygon.build(pb -> { + pb.polygonIdentifier(poly.getPolygonIdentifier().getBase(), poly.getPolygonIdentifier().getYear()); + pb.biogeoclimaticZone(poly.getBiogeoclimaticZone()); + pb.forestInventoryZone(poly.getForestInventoryZone()); + + pb.inventoryTypeGroup(poly.getInventoryTypeGroup()); + pb.targetYear(poly.getTargetYear()); + + pb.mode(poly.getMode()); + pb.percentAvailable(poly.getPercentAvailable()); + + for (var layer : poly.getLayers().values()) { + pb.addLayer(lb -> { + lb.layerType(layer.getLayerType()); + + lb.empiricalRelationshipParameterIndex(layer.getEmpiricalRelationshipParameterIndex()); + + lb.inventoryTypeGroup(layer.getInventoryTypeGroup()); + + lb.loreyHeight(layer.getLoreyHeightByUtilization()); + lb.treesPerHectare(layer.getTreesPerHectareByUtilization()); + lb.quadMeanDiameter(layer.getQuadraticMeanDiameterByUtilization()); + lb.baseArea(layer.getBaseAreaByUtilization()); + + lb.wholeStemVolume(layer.getWholeStemVolumeByUtilization()); + lb.closeUtilizationVolumeByUtilization(layer.getCloseUtilizationVolumeByUtilization()); + lb.closeUtilizationVolumeNetOfDecayByUtilization(layer.getCloseUtilizationVolumeByUtilization()); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + layer.getCloseUtilizationVolumeNetOfDecayByUtilization() + ); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() + ); + + for (var spec : layer.getSpecies().values()) { + lb.addSpecies(sb -> { + sb.genus(spec.getGenus()); + sb.genus(spec.getGenusIndex()); + + sb.breakageGroup(spec.getBreakageGroup()); + sb.volumeGroup(spec.getVolumeGroup()); + sb.decayGroup(spec.getDecayGroup()); + + sb.percentGenus(spec.getPercentGenus()); + + spec.getCvBasalArea(null, null); + + sb.loreyHeight(layer.getLoreyHeightByUtilization()); + sb.treesPerHectare(layer.getTreesPerHectareByUtilization()); + sb.quadMeanDiameter(layer.getQuadraticMeanDiameterByUtilization()); + sb.baseArea(layer.getBaseAreaByUtilization()); + + sb.wholeStemVolume(layer.getWholeStemVolumeByUtilization()); + sb.closeUtilizationVolumeByUtilization(layer.getCloseUtilizationVolumeByUtilization()); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + layer.getCloseUtilizationVolumeByUtilization() + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + layer.getCloseUtilizationVolumeNetOfDecayByUtilization() + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() + ); + + // TODO Compatibility Variables + + spec.getSite().ifPresent(site -> { + + sb.addSite(ib -> { + ib.ageTotal(site.getAgeTotal()); + ib.height(site.getHeight()); + ib.siteCurveNumber(site.getSiteCurveNumber()); + ib.siteIndex(site.getSiteIndex()); + ib.yearsToBreastHeight(site.getYearsToBreastHeight()); + }); + + }); + + }); + } + + lb.primaryGenus(layer.getPrimaryGenus()); + }); + } + }); + + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index ced962a41..8f35cc3fc 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -42,9 +42,9 @@ import ca.bc.gov.nrs.vdyp.model.BecLookup; import ca.bc.gov.nrs.vdyp.model.Coefficients; import ca.bc.gov.nrs.vdyp.model.MatrixMap; -import ca.bc.gov.nrs.vdyp.model.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; /** * Custom Hamcrest Matchers diff --git a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java index 8b8585aab..6bd17bd77 100644 --- a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java +++ b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java @@ -106,7 +106,7 @@ protected ValueOrMarker, EndOfRecord> convert(Map, EndOfRecord> convert(Map, EndOfRecord> convert(Map { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus(genus, controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus(genus); specBuilder.percentGenus(percentGenus); specBuilder.sp64DistributionList(sp64SpeciesDistributions); }); diff --git a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipSpecies.java b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipSpecies.java index 78e2e4582..083938954 100644 --- a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipSpecies.java +++ b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipSpecies.java @@ -75,7 +75,7 @@ protected FipSpecies doBuild() { @Override protected FipSite buildSite(Consumer config) { return FipSite.build(builder -> { - builder.siteGenus(this.genus.get()); + builder.genus(this.genus.get()); builder.polygonIdentifier(this.polygonIdentifier.get()); builder.layerType(this.layerType.get()); config.accept(builder); diff --git a/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java b/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java index 759cde01e..13d18d02d 100644 --- a/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java +++ b/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java @@ -559,7 +559,8 @@ void testFractionGenusCalculationWithSlightError() throws Exception { lb.crownClosure(0.9f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(75f + 0.009f); sb.addSite(ib -> { ib.ageTotal(8f); @@ -570,7 +571,8 @@ void testFractionGenusCalculationWithSlightError() throws Exception { }); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(25f); }); }); @@ -690,7 +692,7 @@ void testProcessVeteranUtilization() throws Exception { siteBuilder.yearsToBreastHeight(7.1f); siteBuilder.ageTotal(97.9f + 7.1f); siteBuilder.siteSpecies("H"); - siteBuilder.siteGenus("H"); + siteBuilder.genus("H"); }); var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", 3, x -> { x.setPercentGenus(22f); @@ -1144,7 +1146,7 @@ void testProcessPrimary() throws Exception { siteBuilder.yearsToBreastHeight(1f); siteBuilder.height(35.3f); siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("D"); + siteBuilder.genus("D"); siteBuilder.siteSpecies("D"); }); var fipSpecies1 = getTestSpecies(polygonId, LayerType.PRIMARY, "B", 3, x -> { @@ -1552,7 +1554,7 @@ void testProcessPrimaryWithOverstory() throws Exception { x.ageTotal(45f); x.height(24.3f); x.siteIndex(28.7f); - x.siteGenus("H"); + x.genus("H"); x.siteSpecies("H"); x.yearsToBreastHeight(5.4f); x.siteCurveNumber(34); @@ -1707,7 +1709,8 @@ void testPass1() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(1); sb.volumeGroup(12); @@ -1717,7 +1720,8 @@ void testPass1() throws ProcessingException { sb.addSp64Distribution("B", 100); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(7); sb.volumeGroup(20); @@ -1727,7 +1731,8 @@ void testPass1() throws ProcessingException { sb.addSp64Distribution("C", 100); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(9); sb.volumeGroup(66); @@ -1737,7 +1742,8 @@ void testPass1() throws ProcessingException { sb.addSp64Distribution("S", 100); }); lb.addSpecies(sb -> { - sb.genus("D", controlMap); + sb.controlMap(controlMap); + sb.genus("D"); sb.percentGenus(74); sb.volumeGroup(25); @@ -1747,7 +1753,8 @@ void testPass1() throws ProcessingException { sb.addSp64Distribution("D", 100); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(9); sb.volumeGroup(37); @@ -1807,7 +1814,8 @@ void testPass2() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(0.8918811f); sb.volumeGroup(12); @@ -1823,7 +1831,8 @@ void testPass2() throws ProcessingException { // sb.wholeStemVolume(6.3858347f); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(11.449178f); sb.volumeGroup(20); @@ -1839,7 +1848,8 @@ void testPass2() throws ProcessingException { // sb.wholeStemVolume(44.700314f); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(9.215943f); sb.volumeGroup(66); @@ -1855,7 +1865,8 @@ void testPass2() throws ProcessingException { // sb.wholeStemVolume(57.47183f); }); lb.addSpecies(sb -> { - sb.genus("D", controlMap); + sb.controlMap(controlMap); + sb.genus("D"); sb.percentGenus(66.05741f); sb.volumeGroup(25); @@ -1871,7 +1882,8 @@ void testPass2() throws ProcessingException { // sb.wholeStemVolume(472.54596f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(12.385582f); sb.volumeGroup(37); @@ -2180,7 +2192,7 @@ void testProcessAsVeteranLayer() throws Exception { x.ageTotal(105f); x.height(26.2f); x.siteIndex(16.7f); - x.siteGenus("H"); + x.genus("H"); x.siteSpecies("H"); x.yearsToBreastHeight(7.1f); }); @@ -2313,7 +2325,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaOneSpecies() throws Exception { layer.getQuadraticMeanDiameterByUtilization().setCoe(0, 33.9379082f); var spec = VdypSpecies.build(layer, builder -> { - builder.genus("Y", controlMap); + builder.controlMap(controlMap); + builder.genus("Y"); builder.percentGenus(100f); builder.volumeGroup(74); builder.decayGroup(63); @@ -2402,7 +2415,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Ex // sp 3, 4, 5, 8, 15 // sp B, C, D, H, S var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(1f); builder.volumeGroup(12); builder.decayGroup(7); @@ -2415,7 +2429,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Ex }); spec1.getLoreyHeightByUtilization().setCoe(0, 38.7456512f); var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("C", controlMap); + builder.controlMap(controlMap); + builder.genus("C"); builder.percentGenus(7f); builder.volumeGroup(20); builder.decayGroup(14); @@ -2424,7 +2439,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Ex spec2.getLoreyHeightByUtilization().setCoe(0, 22.8001652f); var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("D", controlMap); + builder.controlMap(controlMap); + builder.genus("D"); builder.percentGenus(74f); builder.volumeGroup(25); builder.decayGroup(19); @@ -2432,7 +2448,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Ex }); spec3.getLoreyHeightByUtilization().setCoe(0, 33.6889763f); var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("H", controlMap); + builder.controlMap(controlMap); + builder.genus("H"); builder.percentGenus(9f); builder.volumeGroup(37); builder.decayGroup(31); @@ -2440,7 +2457,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1() throws Ex }); spec4.getLoreyHeightByUtilization().setCoe(0, 24.3451157f); var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus("S", controlMap); + builder.controlMap(controlMap); + builder.genus("S"); builder.percentGenus(9f); builder.volumeGroup(66); builder.decayGroup(54); @@ -2681,7 +2699,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1Test2() thro // sp 3, 4, 5, 8, 15 // sp B, C, D, H, S var spec1 = VdypSpecies.build(layer, builder -> { - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(15f); builder.volumeGroup(12); builder.decayGroup(7); @@ -2689,7 +2708,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1Test2() thro }); spec1.getLoreyHeightByUtilization().setCoe(0, 21.5356998f); var spec2 = VdypSpecies.build(layer, builder -> { - builder.genus("D", controlMap); + builder.controlMap(controlMap); + builder.genus("D"); builder.percentGenus(7f); builder.volumeGroup(25); builder.decayGroup(19); @@ -2697,7 +2717,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1Test2() thro }); spec2.getLoreyHeightByUtilization().setCoe(0, 22.4329224f); var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus("H", controlMap); + builder.controlMap(controlMap); + builder.genus("H"); builder.percentGenus(77f); builder.volumeGroup(37); builder.decayGroup(54); @@ -2710,7 +2731,8 @@ void testFindRootsForPrimaryLayerDiameterAndAreaMultipleSpeciesPass1Test2() thro }); spec3.getLoreyHeightByUtilization().setCoe(0, 20.5984688f); var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus("S", controlMap); + builder.controlMap(controlMap); + builder.genus("S"); builder.percentGenus(1f); builder.volumeGroup(66); builder.decayGroup(54); @@ -2937,7 +2959,8 @@ void testCreateVdypPolygon() throws ProcessingException { })); FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("L", controlMap); + builder.controlMap(controlMap); + builder.genus("L"); builder.percentGenus(10f); builder.addSite(siteBuilder -> { siteBuilder.ageTotal(60f); @@ -2949,7 +2972,8 @@ void testCreateVdypPolygon() throws ProcessingException { }); FipSpecies.build(fipPrimaryLayer, builder -> { - builder.genus("PL", controlMap); + builder.controlMap(controlMap); + builder.genus("PL"); builder.percentGenus(90f); }); @@ -2985,7 +3009,8 @@ void testCreateVdypPolygonPercentForestLandGiven() throws ProcessingException { lb.crownClosure(60f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(10f); sb.addSite(siteBuilder -> { @@ -3000,7 +3025,8 @@ void testCreateVdypPolygonPercentForestLandGiven() throws ProcessingException { }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(90f); }); @@ -3042,7 +3068,8 @@ void testCreateVdypPolygonFipYoung() throws ProcessingException { lb.crownClosure(60f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(10f); sb.addSite(ib -> { ib.ageTotal(60f); @@ -3055,7 +3082,8 @@ void testCreateVdypPolygonFipYoung() throws ProcessingException { }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(90f); }); @@ -3097,7 +3125,8 @@ void testApplyStockingFactor() throws ProcessingException { lb.crownClosure(0.9f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.addSite(ib -> { ib.ageTotal(60f); @@ -3109,7 +3138,8 @@ void testApplyStockingFactor() throws ProcessingException { }); }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); }); @@ -3121,7 +3151,8 @@ void testApplyStockingFactor() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3136,7 +3167,8 @@ void testApplyStockingFactor() throws ProcessingException { }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3250,7 +3282,8 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { lb.crownClosure(60f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.addSite(ib -> { ib.ageTotal(60f); @@ -3262,7 +3295,8 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { }); }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); }); @@ -3275,7 +3309,8 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3290,7 +3325,8 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3422,7 +3458,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { lb.crownClosure(60f); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.addSite(ib -> { ib.ageTotal(60f); @@ -3434,7 +3471,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { }); }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); }); }); @@ -3446,7 +3484,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("L", controlMap); + sb.controlMap(controlMap); + sb.genus("L"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3461,7 +3500,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { }); lb.addSpecies(sb -> { - sb.genus("PL", controlMap); + sb.controlMap(controlMap); + sb.genus("PL"); sb.percentGenus(50f); sb.volumeGroup(-1); sb.decayGroup(-1); @@ -3485,7 +3525,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { ); var spec1 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("L", controlMap); + builder.controlMap(controlMap); + builder.genus("L"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); @@ -3505,7 +3546,8 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { ); var spec2 = VdypSpecies.build(vdypLayer, builder -> { - builder.genus("PL", controlMap); + builder.controlMap(controlMap); + builder.genus("PL"); builder.percentGenus(50f); builder.volumeGroup(-1); builder.decayGroup(-1); @@ -3604,7 +3646,8 @@ void testProcessPolygon() throws ProcessingException, IOException { lb.crownClosure(0.9f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(100); sb.addSite(ib -> { @@ -3752,7 +3795,7 @@ FipLayerPrimary getTestPrimaryLayer( siteBuilder.yearsToBreastHeight(7f); siteBuilder.height(6f); siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); siteMutator.accept(siteBuilder); }); @@ -3774,7 +3817,7 @@ FipLayer getTestVeteranLayer( siteBuilder.yearsToBreastHeight(7f); siteBuilder.height(6f); siteBuilder.siteIndex(5f); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); siteMutator.accept(siteBuilder); }); @@ -3794,7 +3837,8 @@ FipSpecies getTestSpecies( var result = FipSpecies.build(builder -> { builder.polygonIdentifier(polygonId); builder.layerType(layer); - builder.genus(genusId, genusIndex); + builder.genus(genusId); + builder.genus(genusIndex); builder.percentGenus(100.0f); builder.addSp64Distribution(genusId, 100f); }); diff --git a/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java b/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java index 26139184c..ffd3a7d8c 100644 --- a/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java +++ b/lib/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java @@ -116,31 +116,36 @@ VdypLayer mockLayer1(Map control) { }); var spec3 = VdypSpecies.build(layer, builder -> { - builder.genus(GenusDefinitionParser.getSpeciesByIndex(3, control).getAlias(), control); // B + builder.controlMap(control); + builder.genus(GenusDefinitionParser.getSpeciesByIndex(3, control).getAlias()); // B builder.percentGenus(20f); }); spec3.getLoreyHeightByUtilization().setAll(38.7456512f); var spec4 = VdypSpecies.build(layer, builder -> { - builder.genus(GenusDefinitionParser.getSpeciesByIndex(4, control).getAlias(), control); // C + builder.controlMap(control); + builder.genus(GenusDefinitionParser.getSpeciesByIndex(4, control).getAlias()); // C builder.percentGenus(20f); }); spec4.getLoreyHeightByUtilization().setAll(22.8001652f); var spec5 = VdypSpecies.build(layer, builder -> { - builder.genus(GenusDefinitionParser.getSpeciesByIndex(5, control).getAlias(), control); // D + builder.controlMap(control); + builder.genus(GenusDefinitionParser.getSpeciesByIndex(5, control).getAlias()); // D builder.percentGenus(20f); }); spec5.getLoreyHeightByUtilization().setAll(33.6889763f); var spec8 = VdypSpecies.build(layer, builder -> { - builder.genus(GenusDefinitionParser.getSpeciesByIndex(8, control).getAlias(), control); // 8 + builder.controlMap(control); + builder.genus(GenusDefinitionParser.getSpeciesByIndex(8, control).getAlias()); // 8 builder.percentGenus(20f); }); spec8.getLoreyHeightByUtilization().setAll(24.3451157f); var spec15 = VdypSpecies.build(layer, builder -> { - builder.genus(GenusDefinitionParser.getSpeciesByIndex(15, control).getAlias(), control); // S + builder.controlMap(control); + builder.genus(GenusDefinitionParser.getSpeciesByIndex(15, control).getAlias()); // S builder.percentGenus(20f); }); spec15.getLoreyHeightByUtilization().setAll(34.6888771f); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index d5e3b0034..e70fefa28 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -75,9 +75,9 @@ * processPolygon for each polygon to be processed. All calls to processPolygon are entirely * independent of one another, allowing (different) polygons to the processed in parallel. */ -public class ForwardProcessingEngine extends ProcessingEngine { +public class ForwardProcessingEngine extends ProcessingEngine { - private static final Logger logger = LoggerFactory.getLogger(ForwardProcessor.class); + private static final Logger logger = LoggerFactory.getLogger(ForwardProcessingEngine.class); private static final int UC_ALL_INDEX = UtilizationClass.ALL.ordinal(); private static final int UC_SMALL_INDEX = UtilizationClass.SMALL.ordinal(); @@ -87,6 +87,49 @@ public class ForwardProcessingEngine extends ProcessingEngine { /** π/4/10⁴ */ public static final float PI_40K = (float) (Math.PI / 40_000); + public enum ForwardExecutionStep implements ProcessingEngine.ExecutionStep { + // Must be first + NONE, // + + CHECK_FOR_WORK, // + CALCULATE_MISSING_SITE_CURVES, // + CALCULATE_COVERAGES, // + DETERMINE_POLYGON_RANKINGS, // + ESTIMATE_MISSING_SITE_INDICES, // + ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES, // + CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX, // + SET_COMPATIBILITY_VARIABLES, // + GROW_1_LAYER_DHDELTA, // + GROW_2_LAYER_BADELTA, // + GROW_3_LAYER_DQDELTA, // + GROW_4_LAYER_BA_AND_DQTPH_EST, // + GROW_5A_LH_EST, // + GROW_5_SPECIES_BADQTPH, // + GROW_6_LAYER_TPH2, // + GROW_7_LAYER_DQ2, // + GROW_8_SPECIES_LH, // + GROW_9_SPECIES_PCT, // + GROW_10_COMPATIBILITY_VARS, // + GROW_11_SPECIES_UC, // + GROW_12_SPECIES_UC_SMALL, // + GROW_13_STORE_SPECIES_DETAILS, // + GROW, // + + // Must be last + ALL; // + + @Override + public ForwardExecutionStep predecessor() throws IllegalStateException { + return Utils.predecessorOrThrow(this, ForwardExecutionStep.values()); + } + + @Override + public ForwardExecutionStep successor() { + return Utils.successorOrThrow(this, ForwardExecutionStep.values()); + } + + } + /* pp */ final ForwardProcessingState fps; /** The entity to which result information is written */ @@ -116,7 +159,7 @@ public ForwardProcessingEngine(Map controlMap) throws Processing * @throws ProcessingException should an error with the data occur during processing */ @Override - public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) throws ProcessingException { + public void processPolygon(VdypPolygon polygon, ForwardExecutionStep lastStepInclusive) throws ProcessingException { logger.info("Starting processing of the primary layer of polygon {}", polygon.getPolygonIdentifier()); @@ -133,14 +176,14 @@ public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) int growTargetControlVariableValue = fps.fcm.getForwardControlVariables() .getControlVariable(ControlVariable.GROW_TARGET_1); if (growTargetControlVariableValue == -1) { - if (polygon.getTargetYear().isEmpty()) { - throw new ProcessingException( - "Control Variable 1 has the value -1, indicating that the grow-to years are" - + " to be read from a grow-to-year file (at " + ControlKey.FORWARD_INPUT_GROWTO.name() - + " in the control file), but no such file was specified." - ); - } - targetYear = polygon.getTargetYear().get(); + targetYear = polygon.getTargetYear().orElseThrow( + () -> new ProcessingException( + "Control Variable 1 has the value -1, indicating that the grow-to years are" + + " to be read from a grow-to-year file (at " + + ControlKey.FORWARD_INPUT_GROWTO.name() + + " in the control file), but no such file was specified." + ) + ); } else { if (growTargetControlVariableValue <= 400) { targetYear = polygon.getPolygonIdentifier().getYear() + growTargetControlVariableValue; @@ -154,7 +197,7 @@ public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) executeForwardAlgorithm(lastStepInclusive, targetYear); } - private void executeForwardAlgorithm(ExecutionStep lastStepInclusive, int stoppingYearInclusive) + private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int stoppingYearInclusive) throws ProcessingException { LayerProcessingState plps = fps.getPrimaryLayerProcessingState(); @@ -162,12 +205,12 @@ private void executeForwardAlgorithm(ExecutionStep lastStepInclusive, int stoppi Optional veteranLayer = Optional .ofNullable(fps.getCurrentPolygon().getLayers().get(LayerType.VETERAN)); - if (lastStepInclusive.ge(ExecutionStep.CHECK_FOR_WORK)) { + if (lastStepInclusive.ge(ForwardExecutionStep.CHECK_FOR_WORK)) { stopIfNoWork(fps); } // SCINXSET - if (lastStepInclusive.ge(ExecutionStep.CALCULATE_MISSING_SITE_CURVES)) { + if (lastStepInclusive.ge(ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES)) { calculateMissingSiteCurves(plps, fps.fcm.getSiteCurveMap()); fps.getVeteranLayerProcessingState().ifPresent(vlps -> { @@ -177,35 +220,35 @@ private void executeForwardAlgorithm(ExecutionStep lastStepInclusive, int stoppi } // VPRIME1, method == 1 - if (lastStepInclusive.ge(ExecutionStep.CALCULATE_COVERAGES)) { + if (lastStepInclusive.ge(ForwardExecutionStep.CALCULATE_COVERAGES)) { calculateCoverages(plps); } - if (lastStepInclusive.ge(ExecutionStep.DETERMINE_POLYGON_RANKINGS)) { + if (lastStepInclusive.ge(ForwardExecutionStep.DETERMINE_POLYGON_RANKINGS)) { determinePolygonRankings(CommonData.PRIMARY_SPECIES_TO_COMBINE); } // SITEADD (TODO: SITEADDU when NDEBUG 11 > 0) - if (lastStepInclusive.ge(ExecutionStep.ESTIMATE_MISSING_SITE_INDICES)) { + if (lastStepInclusive.ge(ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES)) { estimateMissingSiteIndices(plps); } - if (lastStepInclusive.ge(ExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES)) { + if (lastStepInclusive.ge(ForwardExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES)) { estimateMissingYearsToBreastHeightValues(plps); } // VHDOM1 METH_H = 2, METH_A = 2, METH_SI = 2 - if (lastStepInclusive.ge(ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX)) { + if (lastStepInclusive.ge(ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX)) { calculateDominantHeightAgeSiteIndex(plps, fps.fcm.getHl1Coefficients()); } // CVSET1 - if (lastStepInclusive.ge(ExecutionStep.SET_COMPATIBILITY_VARIABLES)) { + if (lastStepInclusive.ge(ForwardExecutionStep.SET_COMPATIBILITY_VARIABLES)) { setCompatibilityVariables(); } // VGROW1 - if (lastStepInclusive.gt(ExecutionStep.SET_COMPATIBILITY_VARIABLES)) { + if (lastStepInclusive.gt(ForwardExecutionStep.SET_COMPATIBILITY_VARIABLES)) { int startingYear = fps.getCurrentStartingYear(); writeCurrentPolygon(startingYear, startingYear, stoppingYearInclusive); @@ -228,7 +271,7 @@ private void executeForwardAlgorithm(ExecutionStep lastStepInclusive, int stoppi // Some unit tests require only some of the grow steps to be executed (and, by // implication, only for the first growth year.) If this is the case, stop // processing now. - if (ExecutionStep.ALL.gt(lastStepInclusive)) { + if (ForwardExecutionStep.ALL.gt(lastStepInclusive)) { break; } @@ -266,10 +309,11 @@ private void executeForwardAlgorithm(ExecutionStep lastStepInclusive, int stoppi * @throws ProcessingException */ private void grow( - LayerProcessingState lps, int currentYear, Optional veteranLayer, ExecutionStep lastStepInclusive + LayerProcessingState lps, int currentYear, Optional veteranLayer, + ForwardExecutionStep lastStepInclusive ) throws ProcessingException { - assert lastStepInclusive.ge(ExecutionStep.GROW_1_LAYER_DHDELTA); + assert lastStepInclusive.ge(ForwardExecutionStep.GROW_1_LAYER_DHDELTA); Bank bank = lps.getBank(); @@ -285,7 +329,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_1_LAYER_DHDELTA.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_1_LAYER_DHDELTA.eq(lastStepInclusive)) return; // (2) Calculate change in basal area (layer) @@ -302,7 +346,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_2_LAYER_BADELTA.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_2_LAYER_BADELTA.eq(lastStepInclusive)) return; // (3) Calculate change in quad-mean-diameter (layer) @@ -315,7 +359,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_3_LAYER_DQDELTA.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_3_LAYER_DQDELTA.eq(lastStepInclusive)) return; int debugSetting9Value = fps.fcm.getDebugSettings() @@ -348,7 +392,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST.eq(lastStepInclusive)) return; // (5) Now calculate per-species (UC All only) end values for basal area, quad-mean-diameter @@ -393,7 +437,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_5A_LH_EST.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_5A_LH_EST.eq(lastStepInclusive)) return; // Now do the actual per-species updates of ba, qmd and tph, based in part @@ -427,7 +471,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_5_SPECIES_BADQTPH.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_5_SPECIES_BADQTPH.eq(lastStepInclusive)) return; // (6) Calculate layer trees-per-hectare, UC All @@ -452,7 +496,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_6_LAYER_TPH2.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_6_LAYER_TPH2.eq(lastStepInclusive)) return; // (7) Calculate layer quad-mean-diameter, uc All @@ -462,7 +506,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_7_LAYER_DQ2.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_7_LAYER_DQ2.eq(lastStepInclusive)) return; // (8) Calculate per-species Lorey heights, uc All @@ -475,7 +519,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_8_SPECIES_LH.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_8_SPECIES_LH.eq(lastStepInclusive)) return; // (9) Calculate basal area percentages per species, uc UC_ALL_INDEX @@ -486,7 +530,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_9_SPECIES_PCT.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_9_SPECIES_PCT.eq(lastStepInclusive)) return; // (10) update the compatibility variables to reflect the changes during the growth period @@ -494,7 +538,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_10_COMPATIBILITY_VARS.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_10_COMPATIBILITY_VARS.eq(lastStepInclusive)) return; // (11) calculate All and the large component volumes to reflect the changes in growth @@ -514,7 +558,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_11_SPECIES_UC.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_11_SPECIES_UC.eq(lastStepInclusive)) return; // (12) calculate the small component volumes to reflect the changes in growth @@ -523,7 +567,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_12_SPECIES_UC_SMALL.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_12_SPECIES_UC_SMALL.eq(lastStepInclusive)) return; // (13) Update the running values. @@ -567,7 +611,7 @@ private void grow( writeCheckpoint(currentYear); - if (ExecutionStep.GROW_13_STORE_SPECIES_DETAILS.eq(lastStepInclusive)) + if (ForwardExecutionStep.GROW_13_STORE_SPECIES_DETAILS.eq(lastStepInclusive)) return; } @@ -3491,4 +3535,14 @@ static int findInventoryTypeGroup(String primarySp0, Optional optionalSe throw new ProcessingException("Unrecognized primary species: " + primarySp0); } } + + @Override + protected ForwardExecutionStep getFirstStep() { + return ForwardExecutionStep.NONE; + } + + @Override + protected ForwardExecutionStep getLastStep() { + return ForwardExecutionStep.ALL; + } } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java index 7514a16c3..81d02dcc2 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java @@ -57,7 +57,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_13_STORE_SPECIES_DETAILS); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_13_STORE_SPECIES_DETAILS); // VDYP7 reports [], -9, -9, 35.473381, -9, -9) Bank bank = fpe.fps.getPrimaryLayerProcessingState().getBank(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index 66ab97818..4c62cc53e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -56,7 +55,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_10_COMPATIBILITY_VARS); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_10_COMPATIBILITY_VARS); // VDYP7 reports [], -9, -9, 35.473381, -9, -9) LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java index 1c16d665c..043f74bb9 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java @@ -27,7 +27,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -116,7 +115,7 @@ void testCurveExtension2() throws ProcessingException { fpe.fps.setPolygon(polygon); - fpe.processPolygon(polygon, ExecutionStep.GROW_1_LAYER_DHDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_1_LAYER_DHDELTA.predecessor()); float hd = 26.5f; int sc = 11; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java index 4783f7a53..a4a307679 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java @@ -14,7 +14,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -56,7 +55,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 54.0f; float hd = 35.2999992f; @@ -76,7 +75,7 @@ void testYoungPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 30.0f; float hd = 10.0f; @@ -96,7 +95,7 @@ void testDebugSettings3EqualsZeroPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 54.0f; float hd = 35.2999992f; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java index c3b947bb5..4be94adbf 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java @@ -16,7 +16,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Reference; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; @@ -59,7 +58,7 @@ void testMixedModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); @@ -90,7 +89,7 @@ void testFiatOnlyModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); /* this value will force the fiat only calculations. */ @@ -121,7 +120,7 @@ void testEmpiricalOnlyModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 1); /* this value will force the empirical only calculations. */ @@ -152,7 +151,7 @@ void testMixedModelWithInterpolation() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); @@ -183,7 +182,7 @@ void testMinimumApplied() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); @@ -214,7 +213,7 @@ void testLimitApplied() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java index 38453bc0e..db1e3addc 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -56,7 +55,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; @@ -84,7 +83,7 @@ void testDebug8Setting2Path() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java index 615faa5fb..be900892b 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -56,7 +55,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_5A_LH_EST); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_5A_LH_EST); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baStart = 45.3864441f; @@ -109,7 +108,7 @@ void testGrowUsingNoSpeciesDynamics() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baChangeRate = 0.00775236264f; @@ -151,7 +150,7 @@ void testGrowUsingFullSpeciesDynamics() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); fpe.fps.fcm.getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 0); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java index 4bfacd730..c15986d64 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -55,7 +54,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_6_LAYER_TPH2); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_6_LAYER_TPH2); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java index aae409868..d244d436c 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -55,7 +54,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_7_LAYER_DQ2); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_7_LAYER_DQ2); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java index 5d9cdd4c2..b92d72702 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java @@ -13,7 +13,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -57,7 +56,7 @@ void testStandardPath() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.fps.fcm.getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 0); - fpe.processPolygon(polygon, ExecutionStep.GROW_8_SPECIES_LH); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); @@ -76,7 +75,7 @@ void testDebug8Setting2() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.fps.fcm.getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); - fpe.processPolygon(polygon, ExecutionStep.GROW_8_SPECIES_LH); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java index 78fac20a9..b809719e2 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java @@ -14,7 +14,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -56,7 +55,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ExecutionStep.GROW_9_SPECIES_PCT); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_9_SPECIES_PCT); LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java index a8bed32cf..1728a6ebf 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java @@ -130,7 +130,7 @@ void testGroupAndStratumNumberSpecialCases() throws IOException, ResourceParseEx var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.DETERMINE_POLYGON_RANKINGS); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.DETERMINE_POLYGON_RANKINGS); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesIndex(), is(1)); assertThrows( @@ -160,7 +160,7 @@ void testCalculateMissingSiteCurves() throws IOException, ResourceParseException var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -196,7 +196,7 @@ void testCalculateMissingSiteCurvesNoSiteCurveData() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -236,7 +236,7 @@ void testEstimateMissingSiteIndicesStep1() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); // Despite 13.40 being in the data stream, the change (2024/8/29) to ignore site information // for all species of the layer except the primary means that method (1) will never be @@ -283,7 +283,7 @@ void testEstimateMissingSiteIndicesStep2() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); var sourceSiteCurve = SiteIndexEquation.SI_CWC_BARKER; var sourceSiteIndex = 13.4f; @@ -314,7 +314,9 @@ void testEstimateMissingYearsToBreastHeightValues() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES); + fpe.processPolygon( + polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES + ); assertThat( fpe.fps.getPrimaryLayerProcessingState().getBank().yearsToBreastHeight, @@ -349,7 +351,7 @@ void testCalculateDominantHeightAgeSiteIndex() throws ProcessingException, IOExc assertThrows( ProcessingException.class, () -> fpe.processPolygon( - polygon, ProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX ) ); } @@ -387,7 +389,9 @@ void testCalculateDominantHeightAgeSiteIndexNoSecondary() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX); + fpe.processPolygon( + polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + ); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesDominantHeight(), is(22.950302f)); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesSiteIndex(), is(34.0f)); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java index 58349eda9..d43b2d080 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java @@ -41,7 +41,7 @@ void testSetCompatibilityVariables() throws ResourceParseException, IOException, var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ProcessingEngine.ExecutionStep.SET_COMPATIBILITY_VARIABLES); + fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.SET_COMPATIBILITY_VARIABLES); // These values have been verified against the FORTRAN implementation, allowing for minor // platform-specific differences. diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java index 1f27c6ac9..76a213b8e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java @@ -32,7 +32,7 @@ void testOnePolygon() throws IOException, ResourceParseException, ProcessingExce var polygon = forwardDataStreamReader.readNextPolygon(); if (polygon.isPresent()) { - fpe.processPolygon(polygon.get(), ExecutionStep.GROW); + fpe.processPolygon(polygon.get(), ForwardProcessingEngine.ForwardExecutionStep.GROW); nPolygonsProcessed += 1; } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/UtilizationOperationsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/UtilizationOperationsTest.java index fa89923b4..89d8113cd 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/UtilizationOperationsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/UtilizationOperationsTest.java @@ -50,11 +50,11 @@ void testUtilizationOperations() throws Exception { lb.layerType(LayerType.PRIMARY); lb.inventoryTypeGroup(1); - lb.baseAreaByUtilization(0.01513f, 0.53100f, 1.27855f, 2.33020f, 40.79285f); - lb.treesPerHectareByUtilization(5.24f, 64.82f, 71.93f, 73.60f, 384.98f); - lb.loreyHeightByUtilization(7.0166f, 30.9724f); - lb.wholeStemVolumeByUtilization(0.0630f, 2.5979f, 9.1057f, 22.4019f, 586.8720f); - lb.quadraticMeanDiameterByUtilization(6.1f, 31.0f, 10.2f, 15.0f, 20.1f, 36.7f); + lb.baseArea(0.01513f, 0.53100f, 1.27855f, 2.33020f, 40.79285f); + lb.treesPerHectare(5.24f, 64.82f, 71.93f, 73.60f, 384.98f); + lb.loreyHeight(7.0166f, 30.9724f); + lb.wholeStemVolume(0.0630f, 2.5979f, 9.1057f, 22.4019f, 586.8720f); + lb.quadMeanDiameter(6.1f, 31.0f, 10.2f, 15.0f, 20.1f, 36.7f); lb.closeUtilizationVolumeByUtilization(0.0630f, 2.5979f, 9.1057f, 22.4019f, 586.8720f); lb.closeUtilizationVolumeNetOfDecayByUtilization(0.0000f, 0.3794f, 6.8469f, 19.8884f, 553.0534f); lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( @@ -68,7 +68,8 @@ void testUtilizationOperations() throws Exception { sb.polygonIdentifier(polyId); sb.layerType(LayerType.PRIMARY); sb.percentGenus(100.0f); - sb.genus(species1Id, 5); + sb.genus(species1Id); + sb.genus(5); sb.baseArea(0.00155f, 0.01412f, 0.05128f, 0.45736f, 28.77972f); sb.treesPerHectare(0.47f, 1.64f, 2.69f, 13.82f, 269.56f); sb.loreyHeight(10.6033f, 33.7440f); @@ -91,7 +92,7 @@ void testUtilizationOperations() throws Exception { tb.siteCurveNumber(13); tb.layerType(LayerType.PRIMARY); tb.polygonIdentifier(polyId); - tb.siteGenus(species1Id); + tb.genus(species1Id); })); })); @@ -99,7 +100,8 @@ void testUtilizationOperations() throws Exception { sb.polygonIdentifier(polyId); sb.layerType(LayerType.PRIMARY); sb.percentGenus(100.0f); - sb.genus(species2Id, 3); + sb.genus(species2Id); + sb.genus(3); sb.baseArea( 0.00000f, 0.00502f, 0.01063f /* <-- too small; this will be replaced by the minimum */, 0.02284f, 0.36143f diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriSiteParser.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriSiteParser.java index b3811026c..44c809105 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriSiteParser.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriSiteParser.java @@ -103,7 +103,7 @@ protected ValueOrMarker, EndOfRecord> convert(Map, EndOfRecord> convert(Map { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus(genus, control); + specBuilder.controlMap(control); + specBuilder.genus(genus); specBuilder.percentGenus(percentGenus); specBuilder.sp64DistributionList(sp64DistributionList); }); diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java index 74db7b0e6..dcb733ae9 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/VriStart.java @@ -50,13 +50,13 @@ import ca.bc.gov.nrs.vdyp.model.BaseVdypSite; import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies; import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies.Builder; +import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.Coefficients; import ca.bc.gov.nrs.vdyp.model.CompatibilityVariableMode; import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; -import ca.bc.gov.nrs.vdyp.model.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.PolygonMode; import ca.bc.gov.nrs.vdyp.model.Region; @@ -471,9 +471,9 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws ); float layerQuadMeanDiameter = quadMeanDiameter(primaryBaseArea, primaryLayerDensity); - lBuilder.quadraticMeanDiameterByUtilization(layerQuadMeanDiameter); - lBuilder.baseAreaByUtilization(primaryBaseArea); - lBuilder.treesPerHectareByUtilization(primaryLayerDensity); + lBuilder.quadMeanDiameter(layerQuadMeanDiameter); + lBuilder.baseArea(primaryBaseArea); + lBuilder.treesPerHectare(primaryLayerDensity); lBuilder.empiricalRelationshipParameterIndex(primaryLayer.getEmpiricalRelationshipParameterIndex()); lBuilder.adaptSpecies(primaryLayer, (sBuilder, vriSpec) -> { @@ -545,7 +545,7 @@ void processPrimaryLayer(VriPolygon polygon, VdypLayer.Builder lBuilder) throws } } - lBuilder.loreyHeightByUtilization(sumBaseAreaLoreyHeight / primaryBaseArea); + lBuilder.loreyHeight(sumBaseAreaLoreyHeight / primaryBaseArea); } diff --git a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java index 5fe523291..c52dc35c7 100644 --- a/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java +++ b/lib/vdyp-vri/src/main/java/ca/bc/gov/nrs/vdyp/vri/model/VriSpecies.java @@ -62,7 +62,7 @@ protected VriSite buildSite(Consumer config) { config.accept(builder); builder.polygonIdentifier(this.polygonIdentifier.get()); builder.layerType(this.layerType.get()); - builder.siteGenus(this.genus); + builder.genus(this.genus); }); } diff --git a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java index 68a051250..de9aaf17c 100644 --- a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java +++ b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/ParsersTogetherTest.java @@ -124,13 +124,14 @@ void testPrimaryOnly() throws IOException, StandProcessingException, ResourcePar speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -193,13 +194,14 @@ void testAddsSpecies() throws IOException, StandProcessingException, ResourcePar speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -262,13 +264,14 @@ void testVeteranOnly() throws IOException, StandProcessingException, ResourcePar speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -341,13 +344,14 @@ void testApplyPercentAvailableToPrimaryLayer() speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -419,13 +423,14 @@ void testPrimaryWithSmallComputedDiameter() throws IOException, StandProcessingE speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -495,23 +500,25 @@ void testFindsPrimaryGenusAndITG() throws IOException, StandProcessingException, speciesStream.addValue(List.of(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(80f); }), VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("S", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("S"); specBuilder.percentGenus(20f); }))); siteStream.addValue(List.of(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }), VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("S"); + siteBuilder.genus("S"); siteBuilder.siteSpecies("S"); }))); @@ -575,23 +582,25 @@ void testFindsGRPBA1() throws IOException, StandProcessingException, ResourcePar speciesStream.addValue(List.of(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(80f); }), VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("S", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("S"); specBuilder.percentGenus(20f); }))); siteStream.addValue(List.of(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }), VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("S"); + siteBuilder.genus("S"); siteBuilder.siteSpecies("S"); }))); @@ -676,13 +685,14 @@ void testDefaultBaAndTphForVeteran( speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); @@ -758,13 +768,14 @@ void testDefaultBaAndTphForVeteranWhenZeroCrownClosure(Float vetBaseArea, Float speciesStream.addValue(Collections.singleton(VriSpecies.build(specBuilder -> { specBuilder.polygonIdentifier(polygonId); specBuilder.layerType(layerType); - specBuilder.genus("B", controlMap); + specBuilder.controlMap(controlMap); + specBuilder.genus("B"); specBuilder.percentGenus(100f); }))); siteStream.addValue(Collections.singleton(VriSite.build(siteBuilder -> { siteBuilder.polygonIdentifier(polygonId); siteBuilder.layerType(layerType); - siteBuilder.siteGenus("B"); + siteBuilder.genus("B"); siteBuilder.siteSpecies("B"); }))); diff --git a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriInputValidationTest.java b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriInputValidationTest.java index 6feb3fab6..88103c8cd 100644 --- a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriInputValidationTest.java +++ b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriInputValidationTest.java @@ -72,7 +72,8 @@ void testPassValid() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -81,7 +82,8 @@ void testPassValid() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -95,7 +97,8 @@ void testPassValid() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -109,7 +112,8 @@ void testPassValid() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -130,7 +134,8 @@ void testPassValid() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -176,7 +181,8 @@ void testFailPrimarySpeciesDontSumTo100() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -185,7 +191,8 @@ void testFailPrimarySpeciesDontSumTo100() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -199,7 +206,8 @@ void testFailPrimarySpeciesDontSumTo100() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.7f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -213,7 +221,8 @@ void testFailPrimarySpeciesDontSumTo100() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -254,7 +263,8 @@ void testFailIfMissingPrimary() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -263,7 +273,8 @@ void testFailIfMissingPrimary() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -277,7 +288,8 @@ void testFailIfMissingPrimary() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.7f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -291,7 +303,8 @@ void testFailIfMissingPrimary() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -350,7 +363,8 @@ void testFailIfPrimaryModeYoungAndLacksAge() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -359,7 +373,8 @@ void testFailIfPrimaryModeYoungAndLacksAge() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -373,7 +388,8 @@ void testFailIfPrimaryModeYoungAndLacksAge() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -387,7 +403,8 @@ void testFailIfPrimaryModeYoungAndLacksAge() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -435,7 +452,8 @@ void testFailIfPrimaryModeYoungAndLacksYearsToBreastHeight() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -444,7 +462,8 @@ void testFailIfPrimaryModeYoungAndLacksYearsToBreastHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -457,7 +476,8 @@ void testFailIfPrimaryModeYoungAndLacksYearsToBreastHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -470,7 +490,8 @@ void testFailIfPrimaryModeYoungAndLacksYearsToBreastHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -520,7 +541,8 @@ void testValidPrimaryModeYoung() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -529,7 +551,8 @@ void testValidPrimaryModeYoung() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -543,7 +566,8 @@ void testValidPrimaryModeYoung() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -557,7 +581,8 @@ void testValidPrimaryModeYoung() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -607,7 +632,8 @@ void testValidPrimaryModeStart() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -616,7 +642,8 @@ void testValidPrimaryModeStart() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -630,7 +657,8 @@ void testValidPrimaryModeStart() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -644,7 +672,8 @@ void testValidPrimaryModeStart() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -693,7 +722,8 @@ void testFailIfPrimaryModeStartMissingHeight() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -702,7 +732,8 @@ void testFailIfPrimaryModeStartMissingHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -716,7 +747,8 @@ void testFailIfPrimaryModeStartMissingHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -730,7 +762,8 @@ void testFailIfPrimaryModeStartMissingHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -782,7 +815,8 @@ void testFailIfSiteIndexMissing(String modeName, String passFail) throws Excepti // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -844,7 +878,8 @@ void testFailIfSiteIndexLow(String modeName, String passFail) throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -908,7 +943,8 @@ void testFailIfAgeTotalMissing(String modeName, String passFail) throws Exceptio // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -971,7 +1007,8 @@ void testFailIfAgeTotalLow(String modeName, String passFail) throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1034,7 +1071,8 @@ void testFailIfBreastHeightAgeLow(String modeName, String passFail) throws Excep // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1100,7 +1138,8 @@ void testFailIfYearsToBreastHeightLow(String modeName, String passFail) throws E // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1173,7 +1212,8 @@ void testFailIfYearsToBreastHeightLow(String modeName, String heightS, String pa // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1237,7 +1277,8 @@ void testFailIfBaseAreaLow(String modeName, String passFail) throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1304,7 +1345,8 @@ void testFailIfTreesPerHectareLow(String modeName, String passFail) throws Excep // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1368,7 +1410,8 @@ void testFailIfCrownClosureLow(String modeName, String passFail) throws Exceptio // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1432,7 +1475,8 @@ void testModeSpecificValidationEverythingOK(String modeName, String passFail) th // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1492,7 +1536,8 @@ void testCheckVeteranHeight() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(3f); sBuilder.addSp64Distribution("BL", 100); sBuilder.addSite(iBuilder -> { @@ -1501,7 +1546,8 @@ void testCheckVeteranHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30f); sBuilder.addSp64Distribution("CW", 100); sBuilder.addSite(iBuilder -> { @@ -1515,7 +1561,8 @@ void testCheckVeteranHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { @@ -1529,7 +1576,8 @@ void testCheckVeteranHeight() throws Exception { }); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1f); sBuilder.addSp64Distribution("SE", 100); sBuilder.addSite(iBuilder -> { @@ -1552,7 +1600,8 @@ void testCheckVeteranHeight() throws Exception { // Species lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(100f); sBuilder.addSp64Distribution("HW", 100); sBuilder.addSite(iBuilder -> { diff --git a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java index 42fc515be..376530126 100644 --- a/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java +++ b/lib/vdyp-vri/src/test/java/ca/bc/gov/nrs/vdyp/vri/VriStartTest.java @@ -140,19 +140,23 @@ void testCompute() throws StandProcessingException { lBuilder.empiricalRelationshipParameterIndex(76); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(2.99999993f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30.0000012f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9000022f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1000009f); }); }); @@ -186,19 +190,23 @@ void testGetCoefficients() throws StandProcessingException { lBuilder.empiricalRelationshipParameterIndex(76); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(2.99999993f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(30.0000012f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(48.9000022f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(18.1000009f); }); }); @@ -260,7 +268,8 @@ void testLowBA() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -313,7 +322,8 @@ void testLowHeight() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -366,7 +376,8 @@ void testNoBA() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -419,7 +430,8 @@ void testNoTPH() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -472,7 +484,8 @@ void testLowRation() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -525,7 +538,8 @@ void testStart() throws Exception { Collection species = List.of(VriSpecies.build(builder -> { builder.polygonIdentifier("Test", 2024); builder.layerType(LayerType.PRIMARY); - builder.genus("B", controlMap); + builder.controlMap(controlMap); + builder.genus("B"); builder.percentGenus(100f); })); var bec = new BecDefinition("IDF", Region.INTERIOR, "Interior Douglas Fir"); @@ -1428,7 +1442,8 @@ void testCompute() throws ProcessingException { lb.polygonIdentifier("Test", 2024); lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(10); sb.volumeGroup(15); sb.decayGroup(11); @@ -1437,7 +1452,8 @@ void testCompute() throws ProcessingException { sb.baseArea(0.634290636f); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(20); sb.volumeGroup(23); sb.decayGroup(15); @@ -1446,7 +1462,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.26858127f); }); lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.percentGenus(30); sb.volumeGroup(33); sb.decayGroup(27); @@ -1455,7 +1472,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(30); sb.volumeGroup(40); sb.decayGroup(33); @@ -1464,7 +1482,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(10); sb.volumeGroup(69); sb.decayGroup(59); @@ -1543,7 +1562,8 @@ void testApply() throws ProcessingException { lb.polygonIdentifier("Test", 2024); lb.layerType(LayerType.PRIMARY); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(10); sb.volumeGroup(15); sb.decayGroup(11); @@ -1552,7 +1572,8 @@ void testApply() throws ProcessingException { sb.baseArea(0.634290636f); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(20); sb.volumeGroup(23); sb.decayGroup(15); @@ -1561,7 +1582,8 @@ void testApply() throws ProcessingException { sb.baseArea(1.26858127f); }); lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.percentGenus(30); sb.volumeGroup(33); sb.decayGroup(27); @@ -1570,7 +1592,8 @@ void testApply() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(30); sb.volumeGroup(40); sb.decayGroup(33); @@ -1579,7 +1602,8 @@ void testApply() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(10); sb.volumeGroup(69); sb.decayGroup(59); @@ -1633,12 +1657,13 @@ void testCompute() throws ProcessingException { VdypLayer layer = VdypLayer.build((lb) -> { lb.polygonIdentifier("Test", 2024); lb.layerType(LayerType.PRIMARY); - lb.baseAreaByUtilization(6.34290648f); - lb.treesPerHectareByUtilization(748.402222f); - lb.quadraticMeanDiameterByUtilization(10.3879938f); - lb.loreyHeightByUtilization(6.61390257f); + lb.baseArea(6.34290648f); + lb.treesPerHectare(748.402222f); + lb.quadMeanDiameter(10.3879938f); + lb.loreyHeight(6.61390257f); lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(10); sb.volumeGroup(15); sb.decayGroup(11); @@ -1647,7 +1672,8 @@ void testCompute() throws ProcessingException { sb.baseArea(0.634290636f); }); lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(20); sb.volumeGroup(23); sb.decayGroup(15); @@ -1656,7 +1682,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.26858127f); }); lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.percentGenus(30); sb.volumeGroup(33); sb.decayGroup(27); @@ -1665,7 +1692,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(30); sb.volumeGroup(40); sb.decayGroup(33); @@ -1674,7 +1702,8 @@ void testCompute() throws ProcessingException { sb.baseArea(1.90287197f); }); lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(10); sb.volumeGroup(69); sb.decayGroup(59); @@ -2007,7 +2036,8 @@ void testProcessPrimary() throws Exception { lb.primaryGenus("F"); // 1 lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(10); sb.addSp64Distribution("BL", 100); sb.addSite(ib -> { @@ -2017,7 +2047,8 @@ void testProcessPrimary() throws Exception { // 2 lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(20); sb.addSp64Distribution("CW", 100); sb.addSite(ib -> { @@ -2028,7 +2059,8 @@ void testProcessPrimary() throws Exception { // 3 lb.addSpecies(sb -> { - sb.genus("F", controlMap); + sb.controlMap(controlMap); + sb.genus("F"); sb.percentGenus(30); sb.addSp64Distribution("FD", 100); sb.addSite(ib -> { @@ -2044,7 +2076,8 @@ void testProcessPrimary() throws Exception { // 4 lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(30); sb.addSp64Distribution("HW", 100); sb.addSite(ib -> { @@ -2055,7 +2088,8 @@ void testProcessPrimary() throws Exception { // 5 lb.addSpecies(sb -> { - sb.genus("S", controlMap); + sb.controlMap(controlMap); + sb.genus("S"); sb.percentGenus(10); sb.addSp64Distribution("S", 100); sb.addSite(ib -> { @@ -2221,7 +2255,8 @@ void testProcessVeteran() throws Exception { lb.primaryGenus("C"); // 1 3 lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(10); sb.addSp64Distribution("BL", 100); sb.addSite(ib -> { @@ -2232,7 +2267,8 @@ void testProcessVeteran() throws Exception { // 2 4 (Primary) lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(50); sb.addSp64Distribution("CW", 100); sb.addSite(ib -> { @@ -2249,7 +2285,8 @@ void testProcessVeteran() throws Exception { // 3 8 lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(40); sb.addSp64Distribution("HW", 100); sb.addSite(ib -> { @@ -2278,7 +2315,8 @@ void testProcessVeteran() throws Exception { lb.primaryGenus("H"); // 3 // 1 3 lb.addSpecies(sb -> { - sb.genus("B", controlMap); + sb.controlMap(controlMap); + sb.genus("B"); sb.percentGenus(20); sb.addSp64Distribution("BL", 100); sb.addSite(ib -> { @@ -2289,7 +2327,8 @@ void testProcessVeteran() throws Exception { // 2 4 lb.addSpecies(sb -> { - sb.genus("C", controlMap); + sb.controlMap(controlMap); + sb.genus("C"); sb.percentGenus(30); sb.addSp64Distribution("CW", 100); sb.addSite(ib -> { @@ -2306,7 +2345,8 @@ void testProcessVeteran() throws Exception { // 3 8 (Primary) lb.addSpecies(sb -> { - sb.genus("H", controlMap); + sb.controlMap(controlMap); + sb.genus("H"); sb.percentGenus(50); sb.addSp64Distribution("HW", 100); sb.addSite(ib -> { @@ -2547,7 +2587,8 @@ void testBasic() throws Exception { lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -2555,7 +2596,8 @@ void testBasic() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -2564,7 +2606,8 @@ void testBasic() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -2578,7 +2621,8 @@ void testBasic() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -2587,7 +2631,8 @@ void testBasic() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -2735,7 +2780,8 @@ void testLowPercentAvailable() throws Exception { lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -2743,7 +2789,8 @@ void testLowPercentAvailable() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -2752,7 +2799,8 @@ void testLowPercentAvailable() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -2766,7 +2814,8 @@ void testLowPercentAvailable() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -2775,7 +2824,8 @@ void testLowPercentAvailable() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -2921,7 +2971,8 @@ void testIncreaseYear() throws Exception { lb.inventoryTypeGroup(3); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -2929,7 +2980,8 @@ void testIncreaseYear() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -2938,7 +2990,8 @@ void testIncreaseYear() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -2952,7 +3005,8 @@ void testIncreaseYear() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -2961,7 +3015,8 @@ void testIncreaseYear() throws Exception { }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3147,7 +3202,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3155,7 +3211,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3164,7 +3221,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3178,7 +3236,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3187,7 +3246,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3342,7 +3402,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3350,7 +3411,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3359,7 +3421,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3373,7 +3436,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3382,7 +3446,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3404,7 +3469,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3412,7 +3478,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3421,7 +3488,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3435,7 +3503,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3444,7 +3513,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3578,7 +3648,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3586,7 +3657,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3595,7 +3667,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3609,7 +3682,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3618,7 +3692,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3777,7 +3852,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3785,7 +3861,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3794,7 +3871,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3808,7 +3886,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3817,7 +3896,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3839,7 +3919,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p lb.empiricalRelationshipParameterIndex(61); lb.addSpecies(spb -> { - spb.genus("B", controlMap); + spb.controlMap(controlMap); + spb.genus("B"); spb.percentGenus(10); spb.addSp64Distribution("BL", 100); spb.addSite(sib -> { @@ -3847,7 +3928,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("C", controlMap); + spb.controlMap(controlMap); + spb.genus("C"); spb.percentGenus(20); spb.addSp64Distribution("CW", 100); spb.addSite(sib -> { @@ -3856,7 +3938,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("F", controlMap); + spb.controlMap(controlMap); + spb.genus("F"); spb.percentGenus(30); spb.addSp64Distribution("FD", 100); spb.addSite(sib -> { @@ -3870,7 +3953,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("H", controlMap); + spb.controlMap(controlMap); + spb.genus("H"); spb.percentGenus(30); spb.addSp64Distribution("HW", 100); spb.addSite(sib -> { @@ -3879,7 +3963,8 @@ controlMap, new ModifierParser(VdypApplicationIdentifier.VRI_START), "mod19813.p }); }); lb.addSpecies(spb -> { - spb.genus("S", controlMap); + spb.controlMap(controlMap); + spb.genus("S"); spb.percentGenus(10); spb.addSp64Distribution("S", 100); spb.addSite(sib -> { @@ -3982,23 +4067,28 @@ void testCompute() throws StandProcessingException { lBuilder.empiricalRelationshipParameterIndex(61); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(10f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(20f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("F", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("F"); sBuilder.percentGenus(30f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(30f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(10f); }); @@ -4033,23 +4123,28 @@ void testBreastHeightAgeLow(float breastHeightAge) throws StandProcessingExcepti lBuilder.empiricalRelationshipParameterIndex(61); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("B", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("B"); sBuilder.percentGenus(10f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("C", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("C"); sBuilder.percentGenus(20f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("F", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("F"); sBuilder.percentGenus(30f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("H", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("H"); sBuilder.percentGenus(30f); }); lBuilder.addSpecies(sBuilder -> { - sBuilder.genus("S", controlMap); + sBuilder.controlMap(controlMap); + sBuilder.genus("S"); sBuilder.percentGenus(10f); }); From 7432075692f91c23113682b5f18a70601747cb59 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 12 Nov 2024 18:52:19 -0800 Subject: [PATCH 25/45] Unit testing framework for model objects --- .../nrs/vdyp/common/ComputationMethods.java | 17 +- .../model/VdypCompatibilityVariables.java | 20 + .../ca/bc/gov/nrs/vdyp/model/VdypSpecies.java | 63 +- .../gov/nrs/vdyp/model/VdypSpeciesTest.java | 8 +- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 371 +++++++++- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 657 ++++++++++++++++++ .../gov/nrs/vdyp/test/VdypMatchersTest.java | 225 ++++++ 7 files changed, 1329 insertions(+), 32 deletions(-) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java index f18583a34..51668b038 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java @@ -21,6 +21,7 @@ import ca.bc.gov.nrs.vdyp.model.CompatibilityVariableMode; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; @@ -159,12 +160,13 @@ public void computeUtilizationComponentsPrimary( ReconcilationMethods.reconcileComponents(basalAreaUtil, treesPerHectareUtil, quadMeanDiameterUtil); if (compatibilityVariableMode != CompatibilityVariableMode.NONE) { + final VdypCompatibilityVariables compatibilityVariables = spec.requireCompatibilityVariables(); float basalAreaSumForSpecies = 0.0f; for (var uc : VdypStartApplication.UTIL_CLASSES) { float currentUcBasalArea = basalAreaUtil.get(uc); - basalAreaUtil.set(uc, currentUcBasalArea + spec.getCvBasalArea(uc, spec.getLayerType())); + basalAreaUtil.set(uc, currentUcBasalArea + compatibilityVariables.getCvBasalArea(uc, spec.getLayerType())); if (basalAreaUtil.get(uc) < 0.0f) { basalAreaUtil.set(uc, 0.0f); } @@ -172,7 +174,7 @@ public void computeUtilizationComponentsPrimary( basalAreaSumForSpecies += basalAreaUtil.get(uc); float newDqValue = quadMeanDiameterUtil.get(uc) - + spec.getCvQuadraticMeanDiameter(uc, spec.getLayerType()); + + compatibilityVariables.getCvQuadraticMeanDiameter(uc, spec.getLayerType()); quadMeanDiameterUtil.set(uc, FloatMath.clamp(newDqValue, uc.lowBound, uc.highBound)); } @@ -213,13 +215,14 @@ public void computeUtilizationComponentsPrimary( if (compatibilityVariableMode == CompatibilityVariableMode.ALL) { // apply compatibility variables to WS volume + final VdypCompatibilityVariables compatibilityVariables = spec.requireCompatibilityVariables(); float wholeStemVolumeSum = 0.0f; for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { wholeStemVolumeUtil.set( uc, wholeStemVolumeUtil.get(uc) * FloatMath - .exp(spec.getCvVolume(uc, VolumeVariable.WHOLE_STEM_VOL, spec.getLayerType())) + .exp(compatibilityVariables.getCvVolume(uc, VolumeVariable.WHOLE_STEM_VOL, spec.getLayerType())) ); wholeStemVolumeSum += wholeStemVolumeUtil.get(uc); } @@ -228,15 +231,13 @@ public void computeUtilizationComponentsPrimary( // Set the adjustment factors for next three volume types for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { adjustCloseUtil - .set(uc, spec.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL, spec.getLayerType())); + .set(uc, compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL, spec.getLayerType())); adjustDecayUtil.set( - uc, spec.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY, spec.getLayerType()) + uc, compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY, spec.getLayerType()) ); adjustDecayWasteUtil.set( uc, - spec.getCvVolume( - uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, spec.getLayerType() - ) + compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, spec.getLayerType()) ); } } else { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java index 98e81f50f..6b45eba00 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java @@ -45,6 +45,22 @@ public Map getCvPrimaryLayerSmall() { return cvPrimaryLayerSmall; } + public float getCvVolume(UtilizationClass uc, VolumeVariable vv, LayerType lt) { + return getCvVolume().get(uc, vv, lt); + } + + public float getCvBasalArea(UtilizationClass uc, LayerType lt) { + return getCvBasalArea().get(uc, lt); + } + + public float getCvQuadraticMeanDiameter(UtilizationClass uc, LayerType lt) { + return getCvQuadraticMeanDiameter().get(uc, lt); + } + + public float getCvPrimaryLayerSmall(UtilizationClassVariable ucv) { + return getCvPrimaryLayerSmall().get(ucv); + } + /** * Accepts a configuration function that accepts a builder to configure. * @@ -155,5 +171,9 @@ public void genus(String speciesGroup) { this.speciesGroup = Optional.of(speciesGroup); } + public void genus(Optional speciesGroup) { + this.speciesGroup = speciesGroup; + } + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java index 21aaf2f90..5b7f85d74 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java @@ -211,24 +211,16 @@ public void setCompatibilityVariables( })); } - public float getCvVolume(UtilizationClass uc, VolumeVariable vv, LayerType lt) { - return requireCompatibilityVariables().getCvVolume().get(uc, vv, lt); + public Optional getCompatibilityVariables() { + return this.compatibilityVariables; } - public float getCvBasalArea(UtilizationClass uc, LayerType lt) { - return requireCompatibilityVariables().getCvBasalArea().get(uc, lt); - } - - public float getCvQuadraticMeanDiameter(UtilizationClass uc, LayerType lt) { - return requireCompatibilityVariables().getCvQuadraticMeanDiameter().get(uc, lt); - } - - public float getCvPrimaryLayerSmall(UtilizationClassVariable ucv) { - return requireCompatibilityVariables().getCvPrimaryLayerSmall().get(ucv); - } - - protected VdypCompatibilityVariables requireCompatibilityVariables() { - return this.compatibilityVariables.orElseThrow( + /** + * @return the compatibility variables if they are present + * @throws InitializationIncompleteException if there are no compatibility variables + */ + public VdypCompatibilityVariables requireCompatibilityVariables() throws InitializationIncompleteException { + return getCompatibilityVariables().orElseThrow( () -> new InitializationIncompleteException( MessageFormat.format("Species {0}: compatibilityVariables", this) ) @@ -342,6 +334,9 @@ public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(UtilizationVec protected UtilizationVector closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = VdypUtilizationHolder .emptyUtilization(); + private Optional> compatibilityVariablesBuilder; + private Optional compatibilityVariables; + @Override public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(UtilizationVector vector) { this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = vector; @@ -449,5 +444,41 @@ public Builder breakageGroup(int i) { this.breakageGroup = Optional.of(i); return this; } + + public void addCompatibilityVariables(Consumer config) { + this.compatibilityVariablesBuilder = Optional.of(config); + this.compatibilityVariables = Optional.empty(); + } + + public void addCompatibilityVariables(VdypCompatibilityVariables cvs) { + this.addCompatibilityVariables(Optional.of(cvs)); + } + + public void addCompatibilityVariables(Optional cvs) { + this.compatibilityVariablesBuilder = Optional.empty(); + this.compatibilityVariables = cvs; + } + + @Override + protected void preProcess() { + super.preProcess(); + + compatibilityVariables = compatibilityVariablesBuilder.map(this::buildCompatibilityVariables).or( + () -> compatibilityVariables + ); + + } + + protected VdypCompatibilityVariables buildCompatibilityVariables( + Consumer config + ) { + return VdypCompatibilityVariables.build(builder -> { + config.accept(builder); + builder.polygonIdentifier(polygonIdentifier.get()); + builder.layerType(layerType.get()); + builder.genus(genus); + }); + } + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java index 3ad9364b4..533136462 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java @@ -1,6 +1,7 @@ package ca.bc.gov.nrs.vdyp.model; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.isPolyId; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; @@ -19,6 +20,7 @@ import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; class VdypSpeciesTest { @@ -123,10 +125,8 @@ void testAdditionalBuildMethods() { List vvs = Arrays.asList(VolumeVariable.values()); List lts = Arrays.asList(LayerType.values()); - assertThrows(InitializationIncompleteException.class, () -> sp.getCvVolume(null, null, null)); - assertThrows(InitializationIncompleteException.class, () -> sp.getCvBasalArea(null, null)); - assertThrows(InitializationIncompleteException.class, () -> sp.getCvQuadraticMeanDiameter(null, null)); - assertThrows(InitializationIncompleteException.class, () -> sp.getCvPrimaryLayerSmall(null)); + assertThrows(InitializationIncompleteException.class, () -> sp.requireCompatibilityVariables()); + assertThat(sp, hasProperty("compatibilityVariables", notPresent())); var cvVolume = new MatrixMap3Impl( ucs, vvs, lts, (x, y, z) -> 1.0f diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index f5bde90e4..1d18ecc74 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -15,6 +16,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.io.Writer; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -30,11 +33,13 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.lang3.StringEscapeUtils; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.BreakageParser; @@ -60,9 +65,15 @@ import ca.bc.gov.nrs.vdyp.model.GenusDefinitionMap; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.Region; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; public class TestUtils { @@ -640,7 +651,7 @@ public static BecDefinition mockBec() { return new BecDefinition("T", Region.COASTAL, "Test"); } - public static void writeToTest(VdypPolygon poly, Appendable out) { + public static VdypPolygon deepCopy(VdypPolygon poly) { var result = VdypPolygon.build(pb -> { pb.polygonIdentifier(poly.getPolygonIdentifier().getBase(), poly.getPolygonIdentifier().getYear()); pb.biogeoclimaticZone(poly.getBiogeoclimaticZone()); @@ -686,8 +697,6 @@ public static void writeToTest(VdypPolygon poly, Appendable out) { sb.percentGenus(spec.getPercentGenus()); - spec.getCvBasalArea(null, null); - sb.loreyHeight(layer.getLoreyHeightByUtilization()); sb.treesPerHectare(layer.getTreesPerHectareByUtilization()); sb.quadMeanDiameter(layer.getQuadraticMeanDiameterByUtilization()); @@ -705,7 +714,57 @@ public static void writeToTest(VdypPolygon poly, Appendable out) { layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() ); - // TODO Compatibility Variables + spec.getCompatibilityVariables().ifPresent(cv -> { + + sb.addCompatibilityVariables(cvb -> { + + MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, + VolumeVariable.ALL, + LayerType.ALL_USED, + (uc, vv, lt) -> cv.getCvVolume(uc, vv, lt) + ); + MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, + LayerType.ALL_USED, + (uc, lt) -> cv.getCvBasalArea(uc, lt) + ); + MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, + LayerType.ALL_USED, + (uc, lt) -> cv.getCvQuadraticMeanDiameter(uc, lt) + ); + Map cvPrimaryLayerSmall = new HashMap<>(); + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var vv : VolumeVariable.ALL) { + for (var lt : LayerType.ALL_USED) { + cvVolume.put(uc, vv, lt, cv.getCvVolume(uc, vv, lt)); + } + } + } + + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + cvBasalArea.put(uc, lt, cv.getCvBasalArea(uc, lt)); + } + } + + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + cvQuadraticMeanDiameter.put(uc, lt, cv.getCvQuadraticMeanDiameter(uc, lt)); + } + } + for (var ucv : UtilizationClassVariable.ALL) { + cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv)); + } + + cvb.cvVolume(cvVolume); + cvb.cvBasalArea(cvBasalArea); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); + }); + + }); spec.getSite().ifPresent(site -> { @@ -726,6 +785,310 @@ public static void writeToTest(VdypPolygon poly, Appendable out) { }); } }); + return result; + } + + private static void line(Appendable out, String line, Object... args) throws UncheckedIOException { + try { + out.append(String.format(line, args)).append("\n"); + } catch (IOException e) { + new UncheckedIOException(e); + } + } + + private static String stringLiteral(String s) { + return String.format("\"%s\"", StringEscapeUtils.escapeJava(s)); + } + + private static String utilVectorLiteral(UtilizationVector uv) { + if (uv.size() == 2) { + return String.format("Utils.heightVector(%f, %f)", uv.getSmall(), uv.getAll()); + } + if (uv.get(UtilizationClass.SMALL) == 0 && + uv.get(UtilizationClass.U75TO125) == 0 && + uv.get(UtilizationClass.U125TO175) == 0 && + uv.get(UtilizationClass.U175TO225) == 0 && + uv.getAll() == uv.getLarge()) { + return String.format("Utils.utilizationVector(%f)", uv.getAll()); + } + if (Math.abs(UtilizationClass.ALL_CLASSES.stream().mapToDouble(uv::get).sum() - uv.getAll()) < 0.00001) { + return String.format( + "Utils.utilizationVector(%f, %f, %f, %f, %f)", + uv.getSmall(), + uv.get(UtilizationClass.U75TO125), + uv.get(UtilizationClass.U125TO175), + uv.get(UtilizationClass.U175TO225), + uv.get(UtilizationClass.OVER225) + ); + } + return String.format( + "Utils.utilizationVector(%f, %f, %f, %f, %f, %f) /* ALL does not match sum of bands */", + uv.getSmall(), + uv.getAll(), + uv.get(UtilizationClass.U75TO125), + uv.get(UtilizationClass.U125TO175), + uv.get(UtilizationClass.U175TO225), + uv.get(UtilizationClass.OVER225) + ); + + } + + private static String enumLiteral(Enum e) { + e.getClass().getName(); + return String.format("%s.%s", e.getClass().getName(), e.toString()); + } + private static String optionalLiteral(Optional opt, Function valueLiteral) { + return opt.map(valueLiteral).map(s -> String.format("Optional.of(%s)", s)).orElse("Optional.empty()"); } + + /** + * Serializes a VdypPolygon as Java code that can be executed to recreate it. Meant to be used to aid in creating + * unit tests. + */ + public static void write(VdypPolygon poly, Appendable out) throws IOException { + try { + line(out, "/* the following Polygon definition was generated */"); + line(out, ""); + line(out, "var result = VdypPolygon.build(pb -> {"); + + line( + out, " pb.polygonIdentifier(%s, %d);", stringLiteral(poly.getPolygonIdentifier().getBase()), poly + .getPolygonIdentifier().getYear() + ); + + line(out, ""); + line(out, " pb.biogeoclimaticZone(getBec(%s));", stringLiteral(poly.getBiogeoclimaticZone().getAlias())); + line(out, " pb.forestInventoryZone(%s);", stringLiteral(poly.getForestInventoryZone())); + line(out, ""); + line(out, " pb.inventoryTypeGroup(%d);", poly.getInventoryTypeGroup()); + line(out, " pb.targetYear(%d);", poly.getTargetYear()); + line(out, ""); + line(out, " pb.mode(%s);", optionalLiteral(poly.getMode(), TestUtils::enumLiteral)); + line(out, " pb.percentAvailable(%f);", poly.getPercentAvailable()); + line(out, ""); + for (var layer : poly.getLayers().values()) { + line(out, " pb.addLayer(lb -> {"); + line(out, " lb.layerType(%s);", enumLiteral(layer.getLayerType())); + line(out, ""); + line( + out, + " lb.empiricalRelationshipParameterIndex(%s);", + optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), i -> Integer.toString(i)) + ); + line(out, ""); + line( + out, + " lb.inventoryTypeGroup(%s);", + optionalLiteral(layer.getInventoryTypeGroup(), i -> Integer.toString(i)) + ); + line(out, ""); + line( + out, + " lb.loreyHeight(%s);", + utilVectorLiteral(layer.getLoreyHeightByUtilization()) + ); + line( + out, + " lb.treesPerHectare(%s);", + utilVectorLiteral(layer.getTreesPerHectareByUtilization()) + ); + line( + out, + " lb.quadMeanDiameter(%s);", + utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) + ); + line( + out, + " lb.baseArea(%s);", + utilVectorLiteral(layer.getBaseAreaByUtilization()) + ); + line(out, ""); + line( + out, + " lb.wholeStemVolume(%s);", + utilVectorLiteral(layer.getWholeStemVolumeByUtilization()) + ); + line( + out, + " lb.closeUtilizationVolumeByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + ); + line( + out, + " lb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) + ); + line( + out, + " lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) + ); + line( + out, + " lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + ); + line(out, ""); + for (var spec : layer.getSpecies().values()) { + line(out, " lb.addSpecies(sb -> {"); + line(out, " sb.genus(%s);", spec.getGenus()); + line(out, " sb.genus(%d);", spec.getGenusIndex()); + line(out, ""); + line(out, " sb.breakageGroup(%d);", spec.getBreakageGroup()); + line(out, " sb.volumeGroup(%d);", spec.getVolumeGroup()); + line(out, " sb.decayGroup(%d);", spec.getDecayGroup()); + line(out, ""); + line(out, " sb.percentGenus(%f);", spec.getPercentGenus()); + line(out, ""); + line(out, " sb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); + line( + out, " sb.treesPerHectare(%s);", utilVectorLiteral( + layer.getTreesPerHectareByUtilization() + ) + ); + line( + out, " sb.quadMeanDiameter(%s);", utilVectorLiteral( + layer.getQuadraticMeanDiameterByUtilization() + ) + ); + line(out, " sb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); + line(out, ""); + line( + out, " sb.wholeStemVolume(%s);", utilVectorLiteral( + layer.getWholeStemVolumeByUtilization() + ) + ); + line( + out, + " sb.closeUtilizationVolumeByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + ); + line( + out, + " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s));", + utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + ); + line( + out, + " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) + ); + line( + out, + " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + ); + line(out, ""); + spec.getCompatibilityVariables().ifPresent(cv -> { + line(out, " sb.addCompatibilityVariables(cvb -> {"); + line(out, ""); + + line( + out, + " MatrixMap3Impl cvVolume = new MatrixMap3Impl<>(" + ); + line(out, " UtilizationClass.UTIL_CLASSES, "); + line(out, " VolumeVariable.ALL, "); + line(out, " LayerType.ALL_USED,"); + line(out, " (uc, vv, lt) -> 0f"); + line(out, " );"); + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var vv : VolumeVariable.ALL) { + for (var lt : LayerType.ALL_USED) { + line( + out, + " cvVolume.put(uc, vv, lt, %f);", + cv.getCvVolume(uc, vv, lt) + ); + } + } + } + line(out, " );"); + line( + out, + " MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>(" + ); + line(out, " UtilizationClass.UTIL_CLASSES, "); + line(out, " LayerType.ALL_USED,"); + line(out, " (uc, lt) -> 0f"); + line(out, " );"); + + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + line( + out, " cvBasalArea.put(uc, lt, %f);", + cv.getCvBasalArea(uc, lt) + ); + } + } + line(out, " );"); + + line( + out, + " MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>(" + ); + line(out, " UtilizationClass.UTIL_CLASSES, "); + line(out, " LayerType.ALL_USED,"); + line(out, " (uc, lt) -> 0f"); + line(out, " );"); + line(out, ""); + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + line( + out, + " cvQuadraticMeanDiameter.put(uc, lt, %f);", + cv.getCvQuadraticMeanDiameter(uc, lt) + ); + } + } + line(out, " );"); + line(out, ""); + line( + out, + " Map cvPrimaryLayerSmall = new HashMap<>();" + ); + line(out, ""); + for (var ucv : UtilizationClassVariable.ALL) { + line(out, " cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv));"); + } + line(out, ""); + line(out, " cvb.cvVolume(cvVolume);"); + line(out, " cvb.cvBasalArea(cvBasalArea);"); + line(out, " cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter);"); + line(out, " cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall);"); + line(out, " });"); + line(out, ""); + }); + line(out, ""); + spec.getSite().ifPresent(site -> { + line(out, ""); + + line(out, " sb.addSite(ib -> {"); + line(out, " ib.ageTotal(%f);", site.getAgeTotal()); + line(out, " ib.height(%f);", site.getHeight()); + line(out, " ib.siteCurveNumber(%d);", site.getSiteCurveNumber()); + line(out, " ib.siteIndex(%f);", site.getSiteIndex()); + line(out, " ib.yearsToBreastHeight(%f);", site.getYearsToBreastHeight()); + line(out, " });"); + line(out, ""); + }); + line(out, ""); + line(out, " });"); + } + line(out, ""); + line(out, "lb.primaryGenus(%s);", optionalLiteral(layer.getPrimaryGenus(), TestUtils::stringLiteral)); + line(out, "});"); + } + line(out, "});"); + + line(out, ""); + + line(out, "/* End of generated polygon Definition */"); + + } catch (UncheckedIOException e) { + throw new IOException(e); + } + } + } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 8f35cc3fc..c2e8fcf1c 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -17,21 +17,25 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.BiFunction; import java.util.function.Function; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; +import org.hamcrest.StringDescription; import org.hamcrest.TypeSafeDiagnosingMatcher; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -44,6 +48,13 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSite; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; /** @@ -613,6 +624,16 @@ public static Matcher isBec(String alias) { return allOf(instanceOf(BecDefinition.class), hasProperty("alias", is(alias))); } + public static Matcher + utilization(Coefficients expected) { + if (expected.size() == 2) { + return utilizationHeight(expected.get(0), expected.get(1)); + } + return utilization( + expected.get(0), expected.get(1), expected.get(2), expected.get(3), expected.get(4), expected.get(5) + ); + } + public static Matcher utilization(float small, float all, float util1, float util2, float util3, float util4) { return new TypeSafeDiagnosingMatcher() { @@ -738,4 +759,640 @@ protected boolean matchesSafely(T item, Description mismatchDescription) { }; } + static private boolean sep(boolean match, Description mismatchDescription) { + if (match) { + mismatchDescription.appendText(", "); + } + return false; + } + + static private boolean matchValue( + boolean match, String name, T value, T expected, Description mismatchDescription + ) { + if (!expected.equals(value)) { + match = sep(match, mismatchDescription); + mismatchDescription + .appendText(name) + .appendText(" was ") + .appendValue(value) + .appendText(" but expected ") + .appendValue(expected); + } + return match; + } + + static private boolean matchValue( + boolean match, String name, UtilizationVector value, UtilizationVector expected, + Description mismatchDescription + ) { + return matchValue(match, name, value, utilization(expected), mismatchDescription); + } + + static private boolean matchValue( + boolean match, String name, T value, Matcher expected, Description mismatchDescription + ) { + if (!expected.matches(value)) { + match = sep(match, mismatchDescription); + mismatchDescription + .appendText(name) + .appendText(" was "); + expected.describeMismatch(expected, mismatchDescription); + } + return match; + } + + Matcher deepEquals(final VdypPolygon expected) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("matches VDYPPolygon ").appendValue(expected.getPolygonIdentifier().toString()); + } + + @Override + protected boolean matchesSafely(VdypPolygon item, Description mismatchDescription) { + boolean match = true; + + match = matchValue( + match, "PolygonIdentifier", + expected.getPolygonIdentifier(), item.getPolygonIdentifier(), + mismatchDescription + ); + + match = matchValue( + match, "BiogeoclimaticZone", + expected.getBiogeoclimaticZone(), item.getBiogeoclimaticZone(), + mismatchDescription + ); + + match = matchValue( + match, "ForestInventoryZone", + expected.getForestInventoryZone(), item.getForestInventoryZone(), + mismatchDescription + ); + + match = matchValue( + match, "InventoryTypeGroup", + expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), + mismatchDescription + ); + + match = matchValue( + match, "Mode", + expected.getMode(), item.getMode(), + mismatchDescription + ); + + match = matchValue( + match, "PercentAvailable", + expected.getPercentAvailable(), item.getPercentAvailable(), + mismatchDescription + ); + + match = matchValue( + match, "TargetYear", + expected.getTargetYear(), item.getTargetYear(), + mismatchDescription + ); + + match = matchValue( + match, "Layers", + expected.getLayers().keySet(), item.getLayers().keySet(), + mismatchDescription + ); + + for (var layerType : expected.getLayers().keySet()) { + if (item.getLayers().keySet().contains(layerType)) { + var itemLayer = item.getLayers().get(layerType); + var expectedLayer = expected.getLayers().get(layerType); + var layerMatcher = deepEquals(expectedLayer); + + if (!layerMatcher.matches(itemLayer)) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText("mismatch in layer "); + mismatchDescription.appendValue(layerType); + mismatchDescription.appendText(": "); + layerMatcher.describeMismatch(itemLayer, mismatchDescription); + } + } + } + + return match; + } + + }; + } + + public static Matcher deepEquals(VdypLayer expected) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("matches VDYPLayer ").appendValue(expected.getPolygonIdentifier()).appendValue( + expected.getLayerType() + ); + } + + @Override + protected boolean matchesSafely(VdypLayer item, Description mismatchDescription) { + boolean match = true; + + match = matchValue( + match, "PolygonIdentifier", + expected.getPolygonIdentifier(), item.getPolygonIdentifier(), + mismatchDescription + ); + match = matchValue( + match, "LayerType", + expected.getLayerType(), item.getLayerType(), + mismatchDescription + ); + + match = matchValue( + match, "InventoryTypeGroup", + expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), + mismatchDescription + ); + match = matchValue( + match, "PrimaryGenus", + expected.getPrimaryGenus(), item.getPrimaryGenus(), + mismatchDescription + ); + match = matchValue( + match, "EmpiricalRelationshipParameterIndex", + expected.getEmpiricalRelationshipParameterIndex(), item + .getEmpiricalRelationshipParameterIndex(), + mismatchDescription + ); + + match = matchValue( + match, "Species Groups", + expected.getSpecies().keySet(), item.getSpecies().keySet(), + mismatchDescription + ); + + for (var speciesGroupId : expected.getSpecies().keySet()) { + if (item.getSpecies().keySet().contains(speciesGroupId)) { + var itemSpecGroup = item.getSpecies().get(speciesGroupId); + var expectedSpecGroup = expected.getSpecies().get(speciesGroupId); + var specGroupMatcher = deepEquals(expectedSpecGroup); + + if (!specGroupMatcher.matches(itemSpecGroup)) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText("mismatch in species group "); + mismatchDescription.appendValue(speciesGroupId); + mismatchDescription.appendText(": "); + specGroupMatcher.describeMismatch(itemSpecGroup, mismatchDescription); + } + } + } + + match = matchValue( + match, "getLoreyHeightByUtilization", + expected.getLoreyHeightByUtilization(), + item.getLoreyHeightByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getBaseAreaByUtilization", + expected.getBaseAreaByUtilization(), + item.getBaseAreaByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getQuadraticMeanDiameterByUtilization", + expected.getQuadraticMeanDiameterByUtilization(), + item.getQuadraticMeanDiameterByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getTreesPerHectareByUtilization", + expected.getTreesPerHectareByUtilization(), + item.getTreesPerHectareByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getWholeStemVolumeByUtilization", + expected.getWholeStemVolumeByUtilization(), + item.getWholeStemVolumeByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getCloseUtilizationVolumeByUtilization", + expected.getCloseUtilizationVolumeByUtilization(), + item.getCloseUtilizationVolumeByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getCloseUtilizationVolumeNetOfDecayByUtilization", + expected.getCloseUtilizationVolumeNetOfDecayByUtilization(), + item.getCloseUtilizationVolumeNetOfDecayByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization", + expected.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), + item.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), + mismatchDescription + ); + match = matchValue( + match, "getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", + expected.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), + item.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), + mismatchDescription + ); + + return match; + } + + }; + } + + public static Matcher deepEquals(VdypSpecies expected) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + + description.appendText("matches VDYPSpecies ") + .appendValue(expected.getPolygonIdentifier()) + .appendValue(expected.getLayerType()) + .appendValue(expected.getGenus()); + } + + @Override + protected boolean matchesSafely(VdypSpecies item, Description mismatchDescription) { + boolean match = true; + + match = matchValue( + match, "PolygonIdentifier", + expected.getPolygonIdentifier(), + item.getPolygonIdentifier(), + mismatchDescription + ); + match = matchValue( + match, "LayerType", + expected.getLayerType(), + item.getLayerType(), + mismatchDescription + ); + match = matchValue( + match, "Genus", + expected.getGenus(), + item.getGenus(), + mismatchDescription + ); + match = matchValue( + match, "GenusIndex", + expected.getGenusIndex(), + item.getGenusIndex(), + mismatchDescription + ); + + match = matchValue( + match, "BreakageGroup", + expected.getBreakageGroup(), + item.getBreakageGroup(), + mismatchDescription + ); + match = matchValue( + match, "VolumeGroup", + expected.getVolumeGroup(), + item.getVolumeGroup(), + mismatchDescription + ); + match = matchValue( + match, "DecayGroup", + expected.getDecayGroup(), + item.getDecayGroup(), + mismatchDescription + ); + + match = matchValue( + match, "PercentGenus", + expected.getPercentGenus(), + item.getPercentGenus(), + mismatchDescription + ); + match = matchValue( + match, "FractionGenus", + expected.getFractionGenus(), + item.getFractionGenus(), + mismatchDescription + ); + + if (expected.getSite().isEmpty() && item.getSite().isPresent()) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText("expected not to have a Site but one was present"); + } else if (expected.getSite().isPresent() && item.getSite().isEmpty()) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText("expected to have a Site but none was present"); + } + match = Utils.flatMapBoth(expected.getSite(), item.getSite(), (expectedSite, itemSite) -> { + var siteMatcher = deepEquals(expectedSite); + if (!siteMatcher.matches(itemSite)) { + mismatchDescription.appendText("mismatch in site "); + mismatchDescription.appendValue(expected.getGenus()); + mismatchDescription.appendText(": "); + siteMatcher.describeMismatch(itemSite, mismatchDescription); + return Optional.of(false); + } + return Optional.empty(); + }).orElse(match); + + if (expected.getSite().isEmpty() && item.getSite().isPresent()) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText( + "expected not to have Compatibility Variables but they were present" + ); + } else if (expected.getSite().isPresent() && item.getSite().isEmpty()) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText("expected to have Compatibility Variables but none were present"); + } + + Utils.flatMapBoth( + expected.getCompatibilityVariables(), item.getCompatibilityVariables(), ( + expectedCv, itemCv + ) -> { + var cvMatcher = deepEquals(expectedCv); + + if (!cvMatcher.matches(itemCv)) { + mismatchDescription.appendText("mismatch in Compatibility Variables "); + mismatchDescription.appendValue(expected.getGenus()); + mismatchDescription.appendText(": "); + cvMatcher.describeMismatch(itemCv, mismatchDescription); + return Optional.of(false); + } + return Optional.empty(); + + } + ); + expected.getSp64DistributionSet(); + + return match; + + } + + }; + } + + public static Matcher deepEquals(VdypCompatibilityVariables expected) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + + description.appendText("Matches given VDYPCompatibilityVariables"); + } + + @Override + protected boolean matchesSafely(VdypCompatibilityVariables item, Description mismatchDescription) { + boolean match = true; + + match = matchValue( + match, "CvBasalArea", + item.getCvVolume(), + mmEquals(expected.getCvVolume(), VdypMatchers::closeTo), + mismatchDescription + ); + match = matchValue( + match, "CvBasalArea", + item.getCvBasalArea(), + mmEquals(expected.getCvBasalArea(), VdypMatchers::closeTo), + mismatchDescription + ); + match = matchValue( + match, "CvBasalArea", + item.getCvQuadraticMeanDiameter(), + mmEquals(expected.getCvQuadraticMeanDiameter(), VdypMatchers::closeTo), + mismatchDescription + ); + match = matchValue( + match, "CvBasalArea", + item.getCvPrimaryLayerSmall(), + mapEquals(expected.getCvPrimaryLayerSmall(), VdypMatchers::closeTo), + mismatchDescription + ); + + return match; + } + }; + } + + public static Matcher> mapEquals( + Map expected, Function> valueMatcherGenerator + ) { + if (expected.isEmpty()) { + return Matchers.anEmptyMap(); + } + return new TypeSafeDiagnosingMatcher>() { + + @Override + public void describeTo(Description description) { + + description.appendText("map with contents: ").appendValue(expected); + + } + + @Override + protected boolean matchesSafely( + Map item, Description mismatchDescription + ) { + if (item.isEmpty()) { + mismatchDescription.appendText("map was empty"); + return false; + } + if (!expected.keySet().equals(item.keySet())) { + mismatchDescription.appendText("expected keys ").appendValue(expected.keySet()).appendText( + " but were " + ).appendValue(item.keySet()); + return false; + } + List failures = new LinkedList<>(); + for (var key : expected.keySet()) { + final V expectedValue = expected.get(key); + var valueMatcher = valueMatcherGenerator.apply(expectedValue); + V actualValue = item.get(key); + if (!valueMatcher.matches(item.get(key))) { + var failureDescription = new StringDescription(); + failureDescription.appendText("at "); + failureDescription.appendValue(key); + failureDescription.appendText(" expected "); + failureDescription.appendValue(expectedValue); + failureDescription.appendText(" but it "); + valueMatcher.describeMismatch(actualValue, failureDescription); + failures.add(failureDescription.toString()); + } + } + + if (!failures.isEmpty()) { + var first = failures.iterator().next(); + mismatchDescription.appendText(first); + + if (failures.size() > 1) { + mismatchDescription + .appendText(" and there were ") + .appendText(Integer.toString(failures.size() - 1)) + .appendText(" other mismatches"); + } + return false; + } + + return true; + } + + }; + } + + public static , T> Matcher mmEquals( + M expected, Function> valueMatcherGenerator + ) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("a matrix map identical to that given"); + } + + @Override + protected boolean matchesSafely(M item, Description mismatchDescription) { + if (item.getNumDimensions() != expected.getNumDimensions()) { + mismatchDescription + .appendText("matrix map had ") + .appendText(Integer.toString(item.getNumDimensions())) + .appendText(" dimensions but expected ") + .appendText(Integer.toString(expected.getNumDimensions())); + + return false; + } + if (!item.getDimensions().equals(expected.getDimensions())) { + mismatchDescription + .appendText("matrix map had dimensions ") + .appendText(item.getDimensions().toString()) + .appendText(" but expected ") + .appendText(expected.getDimensions().toString()); + + return false; + } + + List failures = new LinkedList<>(); + + expected.eachKey(key -> { + T expectedValue = expected.getM(key); + T actualValue = item.getM(key); + var valueMatcher = valueMatcherGenerator.apply(expectedValue); + if (!valueMatcher.matches(actualValue)) { + var failureDescription = new StringDescription(); + failureDescription.appendText("at "); + failureDescription.appendValueList("[", ", ", "]", key); + failureDescription.appendText(" expected "); + failureDescription.appendValue(expectedValue); + failureDescription.appendText(" but it "); + valueMatcher.describeMismatch(actualValue, failureDescription); + failures.add(failureDescription.toString()); + } + + }); + + if (!failures.isEmpty()) { + var first = failures.iterator().next(); + mismatchDescription.appendText(first); + + if (failures.size() > 1) { + mismatchDescription + .appendText(" and there were ") + .appendText(Integer.toString(failures.size() - 1)) + .appendText(" other mismatches"); + } + return false; + } + + return true; + } + + }; + } + + public static Matcher deepEquals(VdypSite expected) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + + description.appendText("matches VDYPSite ") + .appendValue(expected.getPolygonIdentifier()) + .appendValue(expected.getLayerType()) + .appendValue(expected.getSiteGenus()); + } + + @Override + protected boolean matchesSafely(VdypSite item, Description mismatchDescription) { + boolean match = true; + match = matchValue( + match, "PolygonIdentifier", + expected.getPolygonIdentifier(), + item.getPolygonIdentifier(), + mismatchDescription + ); + match = matchValue( + match, "LayerType", + expected.getLayerType(), + item.getLayerType(), + mismatchDescription + ); + match = matchValue( + match, "Genus", + expected.getSiteGenus(), + item.getSiteGenus(), + mismatchDescription + ); + + match = matchValue( + match, "LayerType", + expected.getLayerType(), + item.getLayerType(), + mismatchDescription + ); + + match = matchValue( + match, "AgeTotal", + expected.getAgeTotal(), + item.getAgeTotal(), + mismatchDescription + ); + match = matchValue( + match, "Height", + expected.getHeight(), + item.getHeight(), + mismatchDescription + ); + match = matchValue( + match, "SiteCurveNumber", + expected.getSiteCurveNumber(), + item.getSiteCurveNumber(), + mismatchDescription + ); + match = matchValue( + match, "SiteIndex", + expected.getSiteIndex(), + item.getSiteIndex(), + mismatchDescription + ); + match = matchValue( + match, "YearsAtBreastHeight", + expected.getYearsAtBreastHeight(), + item.getYearsAtBreastHeight(), + mismatchDescription + ); + match = matchValue( + match, "YearsToBreastHeight", + expected.getYearsToBreastHeight(), + item.getYearsToBreastHeight(), + mismatchDescription + ); + + return match; + } + }; + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java new file mode 100644 index 000000000..d477675cf --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -0,0 +1,225 @@ +package ca.bc.gov.nrs.vdyp.test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.matchesRegex; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.hamcrest.StringDescription; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.MatrixMap; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; + +class VdypMatchersTest { + + static void assertMismatch(T item, Matcher unit, Matcher descriptionMatcher) { + assertFalse(unit.matches(item), "expected match to fail but it passed"); + var description = new StringDescription(); + unit.describeMismatch(item, description); + assertThat( + "Mismatch description not as expected", + description.toString(), descriptionMatcher + ); + } + + static void assertMatch(T item, Matcher unit) { + var result = unit.matches(item); + if (result) { + return; + } + + var description = new StringDescription(); + unit.describeMismatch(item, description); + assertFalse(result, "Expected match to pass but it failed with description: " + description.toString()); + } + + @Nested + class mmEquals { + + MatrixMap2 expected; + + MatrixMap2 test; + + @BeforeEach + void setup() { + expected = new MatrixMap2Impl<>( + List.of(1, 2, 3), List.of(4, 5, 6), (k1, k2) -> String.format("%d:%d", k1, k2) + ); + test = new MatrixMap2Impl<>(List.of(1, 2, 3), List.of(4, 5, 6), (k1, k2) -> String.format("%d:%d", k1, k2)); + } + + @Test + void testEqual() { + var unit = VdypMatchers.mmEquals(expected, s -> Matchers.equalTo(s)); + assertMatch(test, unit); + } + + @Test + void testOneCellDifferent() { + test.put(1, 4, "DIFFERENT"); + var unit = VdypMatchers.mmEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch(test, unit, equalTo("at [<1>, <4>] expected \"1:4\" but it was \"DIFFERENT\"")); + } + + @Test + void testTwoCellsDifferent() { + test.put(1, 4, "DIFFERENT"); + test.put(2, 5, "DIFFERENT"); + var unit = VdypMatchers.mmEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo( + "at [<1>, <4>] expected \"1:4\" but it was \"DIFFERENT\" and there were 1 other mismatches" + ) + ); + } + + @Test + void testDifferentDimensionality() { + MatrixMap test3 = new MatrixMap3Impl( + List.of(1), List.of(2), List.of(3), (k1, k2, k3) -> "TEST" + ); + var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); + assertMismatch( + test3, unit, equalTo( + "matrix map had 3 dimensions but expected 2" + ) + ); + } + + @Test + void testDimensionMissingValue() { + test = new MatrixMap2Impl<>(List.of(1, 2), List.of(4, 5, 6), (k1, k2) -> String.format("%d:%d", k1, k2)); + + var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo( + "matrix map had dimensions [[1, 2], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]" + ) + ); + } + + @Test + void testDimensionExtraValue() { + test = new MatrixMap2Impl<>( + List.of(0, 1, 2, 3), List.of(4, 5, 6), (k1, k2) -> String.format("%d:%d", k1, k2) + ); + + var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo( + "matrix map had dimensions [[0, 1, 2, 3], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]" + ) + ); + } + + } + + @Nested + class mapEquals { + + Map expected; + + Map test; + + @BeforeEach + void setup() { + expected = Utils.constMap(map -> { + map.put(1, "Value 1"); + map.put(2, "Value 2"); + }); + test = new HashMap<>(expected); + } + + @Test + void testEqual() { + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMatch(test, unit); + } + + @Test + void testOneEntryDifferent() { + test.put(1, "DIFFERENT"); + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch(test, unit, matchesRegex("at <\\d> expected \"Value \\d\" but it was \"DIFFERENT\"")); + } + + @Test + void testTwoEntriesDifferent() { + test.put(1, "DIFFERENT"); + test.put(2, "DIFFERENT"); + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, matchesRegex( + "at <\\d> expected \"Value \\d\" but it was \"DIFFERENT\" and there were 1 other mismatches" + ) + ); + } + + @Test + void testMissingEntry() { + test.remove(1); + + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo( + "expected keys <[1, 2]> but were <[2]>" + ) + ); + } + + @Test + void testExtraEntry() { + test.put(42, "EXTRA"); + + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo( + "expected keys <[1, 2]> but were <[1, 2, 42]>" + ) + ); + } + + @Test + void testBothEmpty() { + expected = new HashMap<>(); + test = new HashMap<>(); + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMatch( + test, unit + ); + } + + @Test + void testExpectedEmpty() { + expected = new HashMap<>(); + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo("map size was <2>") + ); + } + + @Test + void testActualEmpty() { + test = new HashMap<>(); + var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); + assertMismatch( + test, unit, equalTo("map was empty") + ); + } + + } + +} From 576ae44a262dbe53bcd0efa3f432ceac5c94ff34 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 13 Nov 2024 15:12:36 -0800 Subject: [PATCH 26/45] Unit tests for deepEquals matcher for VdypSite --- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 77 ++++----- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 151 ++++++++++++++++++ 2 files changed, 192 insertions(+), 36 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index c2e8fcf1c..d6a2974c5 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -760,14 +760,14 @@ protected boolean matchesSafely(T item, Description mismatchDescription) { } static private boolean sep(boolean match, Description mismatchDescription) { - if (match) { + if (!match) { mismatchDescription.appendText(", "); } return false; } static private boolean matchValue( - boolean match, String name, T value, T expected, Description mismatchDescription + boolean match, String name, T expected, T value, Description mismatchDescription ) { if (!expected.equals(value)) { match = sep(match, mismatchDescription); @@ -782,14 +782,14 @@ static private boolean matchValue( } static private boolean matchValue( - boolean match, String name, UtilizationVector value, UtilizationVector expected, + boolean match, String name, UtilizationVector expected, UtilizationVector value, Description mismatchDescription ) { return matchValue(match, name, value, utilization(expected), mismatchDescription); } static private boolean matchValue( - boolean match, String name, T value, Matcher expected, Description mismatchDescription + boolean match, String name, Matcher expected, T value, Description mismatchDescription ) { if (!expected.matches(value)) { match = sep(match, mismatchDescription); @@ -815,49 +815,57 @@ protected boolean matchesSafely(VdypPolygon item, Description mismatchDescriptio match = matchValue( match, "PolygonIdentifier", - expected.getPolygonIdentifier(), item.getPolygonIdentifier(), + expected.getPolygonIdentifier(), + item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( match, "BiogeoclimaticZone", - expected.getBiogeoclimaticZone(), item.getBiogeoclimaticZone(), + expected.getBiogeoclimaticZone(), + item.getBiogeoclimaticZone(), mismatchDescription ); match = matchValue( match, "ForestInventoryZone", - expected.getForestInventoryZone(), item.getForestInventoryZone(), + expected.getForestInventoryZone(), + item.getForestInventoryZone(), mismatchDescription ); match = matchValue( match, "InventoryTypeGroup", - expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), + expected.getInventoryTypeGroup(), + item.getInventoryTypeGroup(), mismatchDescription ); match = matchValue( match, "Mode", - expected.getMode(), item.getMode(), + expected.getMode(), + item.getMode(), mismatchDescription ); match = matchValue( match, "PercentAvailable", - expected.getPercentAvailable(), item.getPercentAvailable(), + expected.getPercentAvailable(), + item.getPercentAvailable(), mismatchDescription ); match = matchValue( match, "TargetYear", - expected.getTargetYear(), item.getTargetYear(), + expected.getTargetYear(), + item.getTargetYear(), mismatchDescription ); match = matchValue( match, "Layers", - expected.getLayers().keySet(), item.getLayers().keySet(), + expected.getLayers().keySet(), + item.getLayers().keySet(), mismatchDescription ); @@ -899,29 +907,33 @@ protected boolean matchesSafely(VdypLayer item, Description mismatchDescription) match = matchValue( match, "PolygonIdentifier", - expected.getPolygonIdentifier(), item.getPolygonIdentifier(), + expected.getPolygonIdentifier(), + item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( match, "LayerType", - expected.getLayerType(), item.getLayerType(), + expected.getLayerType(), + item.getLayerType(), mismatchDescription ); match = matchValue( match, "InventoryTypeGroup", - expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), + expected.getInventoryTypeGroup(), + item.getInventoryTypeGroup(), mismatchDescription ); match = matchValue( match, "PrimaryGenus", - expected.getPrimaryGenus(), item.getPrimaryGenus(), + expected.getPrimaryGenus(), + item.getPrimaryGenus(), mismatchDescription ); match = matchValue( match, "EmpiricalRelationshipParameterIndex", - expected.getEmpiricalRelationshipParameterIndex(), item - .getEmpiricalRelationshipParameterIndex(), + expected.getEmpiricalRelationshipParameterIndex(), + item.getEmpiricalRelationshipParameterIndex(), mismatchDescription ); @@ -1151,26 +1163,26 @@ protected boolean matchesSafely(VdypCompatibilityVariables item, Description mis match = matchValue( match, "CvBasalArea", - item.getCvVolume(), mmEquals(expected.getCvVolume(), VdypMatchers::closeTo), + item.getCvVolume(), mismatchDescription ); match = matchValue( match, "CvBasalArea", - item.getCvBasalArea(), mmEquals(expected.getCvBasalArea(), VdypMatchers::closeTo), + item.getCvBasalArea(), mismatchDescription ); match = matchValue( match, "CvBasalArea", - item.getCvQuadraticMeanDiameter(), mmEquals(expected.getCvQuadraticMeanDiameter(), VdypMatchers::closeTo), + item.getCvQuadraticMeanDiameter(), mismatchDescription ); match = matchValue( match, "CvBasalArea", - item.getCvPrimaryLayerSmall(), mapEquals(expected.getCvPrimaryLayerSmall(), VdypMatchers::closeTo), + item.getCvPrimaryLayerSmall(), mismatchDescription ); @@ -1341,19 +1353,12 @@ protected boolean matchesSafely(VdypSite item, Description mismatchDescription) mismatchDescription ); match = matchValue( - match, "Genus", + match, "SpeciesGroup", expected.getSiteGenus(), item.getSiteGenus(), mismatchDescription ); - match = matchValue( - match, "LayerType", - expected.getLayerType(), - item.getLayerType(), - mismatchDescription - ); - match = matchValue( match, "AgeTotal", expected.getAgeTotal(), @@ -1378,18 +1383,18 @@ protected boolean matchesSafely(VdypSite item, Description mismatchDescription) item.getSiteIndex(), mismatchDescription ); - match = matchValue( - match, "YearsAtBreastHeight", - expected.getYearsAtBreastHeight(), - item.getYearsAtBreastHeight(), - mismatchDescription - ); match = matchValue( match, "YearsToBreastHeight", expected.getYearsToBreastHeight(), item.getYearsToBreastHeight(), mismatchDescription ); + match = matchValue( + match, "YearsAtBreastHeight", + expected.getYearsAtBreastHeight(), + item.getYearsAtBreastHeight(), + mismatchDescription + ); return match; } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index d477675cf..731e21943 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -18,10 +18,12 @@ import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.VdypSite; class VdypMatchersTest { @@ -46,6 +48,155 @@ static void assertMatch(T item, Matcher unit) { assertFalse(result, "Expected match to pass but it failed with description: " + description.toString()); } + @Nested + class deepEquals { + @Nested + class testVdypSite { + VdypSite expected; + Matcher unit; + + @BeforeEach + void setup() { + expected = VdypSite.build(ib -> { + ib.polygonIdentifier("Test", 2024); + ib.layerType(LayerType.PRIMARY); + ib.genus("MB"); + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + unit = VdypMatchers.deepEquals(expected); + } + + @Test + void testPass() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + }); + + assertMatch(actual, unit); + } + + @Test + void testPolyIdDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.polygonIdentifier("Different", 2025); + }); + + assertMismatch( + actual, unit, equalTo( + "PolygonIdentifier was but expected " + ) + ); + } + + @Test + void testLayerTypeDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.layerType(LayerType.VETERAN); + }); + + assertMismatch( + actual, unit, equalTo( + "LayerType was but expected " + ) + ); + } + + @Test + void testSpeciesGroupDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.genus("B"); + }); + + assertMismatch( + actual, unit, equalTo( + "SpeciesGroup was \"B\" but expected \"MB\"" + ) + ); + } + + @Test + void testHeightDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.height(16); + }); + + assertMismatch( + actual, unit, equalTo( + "Height was but expected " + ) + ); + } + + @Test + void testAgeTotalDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.ageTotal(45); + }); + + // Years at breast height is computed from AgeTotal and YearstToBreastHeight + assertMismatch( + actual, unit, equalTo( + "AgeTotal was but expected , YearsAtBreastHeight was but expected " + ) + ); + } + + @Test + void testYearsToBreastHeightDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.yearsToBreastHeight(4); + }); + + // Years at breast height is computed from AgeTotal and YearstToBreastHeight + assertMismatch( + actual, unit, equalTo( + "YearsToBreastHeight was but expected , YearsAtBreastHeight was but expected " + ) + ); + } + + @Test + void testSiteCurveNumberDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.siteCurveNumber(41); + }); + + assertMismatch( + actual, unit, equalTo( + "SiteCurveNumber was but expected " + ) + ); + } + + @Test + void testSiteIndexDifferent() { + var actual = VdypSite.build(ib -> { + ib.copy(expected); + ib.siteIndex(3); + }); + + assertMismatch( + actual, unit, equalTo( + "SiteIndex was but expected " + ) + ); + } + + } + } + @Nested class mmEquals { From 7f35fd785fad16b5af25506e85bdd2cad2ac2132 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 13 Nov 2024 16:53:54 -0800 Subject: [PATCH 27/45] Unit tests for deepEquals matcher for VdypCompatibilityVariables --- .../model/VdypCompatibilityVariables.java | 49 +++++++- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 10 +- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 113 +++++++++++++++++- 3 files changed, 159 insertions(+), 13 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java index 6b45eba00..79385f954 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java @@ -2,10 +2,14 @@ import java.text.MessageFormat; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Function; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl.TriFunction; import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.builders.SpeciesGroupIdentifiedBuilder; @@ -108,18 +112,54 @@ public void polygonIndentifier(PolygonIdentifier polyId) { private Optional> cvQuadraticMeanDiameter = Optional.empty(); private Optional> cvPrimaryLayerSmall = Optional.empty(); + public void cvVolume(TriFunction init) { + MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, + VolumeVariable.ALL, + LayerType.ALL_USED, + init + ); + cvVolume(cvVolume); + } + public void cvVolume(MatrixMap3 cvVolume) { this.cvVolume = Optional.of(cvVolume); } + public void cvBasalArea(BiFunction init) { + MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, + LayerType.ALL_USED, + init + ); + cvBasalArea(cvBasalArea); + } + public void cvBasalArea(MatrixMap2 cvBasalArea) { this.cvBasalArea = Optional.of(cvBasalArea); } + public void cvQuadraticMeanDiameter(BiFunction init) { + MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, + LayerType.ALL_USED, + init + ); + cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + } + public void cvQuadraticMeanDiameter(MatrixMap2 cvQuadraticMeanDiameter) { this.cvQuadraticMeanDiameter = Optional.of(cvQuadraticMeanDiameter); } + public void cvPrimaryLayerSmall(Function init) { + var cvPrimaryLayerSmall = new HashMap(); + for (var ucv : UtilizationClassVariable.ALL) { + cvPrimaryLayerSmall.put(ucv, init.apply(ucv)); + } + cvPrimaryLayerSmall(cvPrimaryLayerSmall); + } + public void cvPrimaryLayerSmall(Map cvPrimaryLayerSmall) { this.cvPrimaryLayerSmall = Optional.of(cvPrimaryLayerSmall); } @@ -143,10 +183,11 @@ protected String getBuilderId() { } public void copy(VdypCompatibilityVariables toCopy) { - cvVolume(toCopy.getCvVolume()); - cvBasalArea(toCopy.getCvBasalArea()); - cvQuadraticMeanDiameter(toCopy.getCvQuadraticMeanDiameter()); - cvPrimaryLayerSmall(toCopy.getCvPrimaryLayerSmall()); + // Do a deep copy of the contents of the matrix maps + cvVolume(toCopy.getCvVolume()::getM); + cvBasalArea(toCopy.getCvBasalArea()::getM); + cvQuadraticMeanDiameter(toCopy.getCvQuadraticMeanDiameter()::getM); + cvPrimaryLayerSmall(toCopy.getCvPrimaryLayerSmall()::get); } @Override diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index d6a2974c5..e64095ed7 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -795,8 +795,8 @@ static private boolean matchValue( match = sep(match, mismatchDescription); mismatchDescription .appendText(name) - .appendText(" was "); - expected.describeMismatch(expected, mismatchDescription); + .appendText(" "); + expected.describeMismatch(value, mismatchDescription); } return match; } @@ -1162,7 +1162,7 @@ protected boolean matchesSafely(VdypCompatibilityVariables item, Description mis boolean match = true; match = matchValue( - match, "CvBasalArea", + match, "CvVolume", mmEquals(expected.getCvVolume(), VdypMatchers::closeTo), item.getCvVolume(), mismatchDescription @@ -1174,13 +1174,13 @@ protected boolean matchesSafely(VdypCompatibilityVariables item, Description mis mismatchDescription ); match = matchValue( - match, "CvBasalArea", + match, "CvQuadraticMeanDiameter", mmEquals(expected.getCvQuadraticMeanDiameter(), VdypMatchers::closeTo), item.getCvQuadraticMeanDiameter(), mismatchDescription ); match = matchValue( - match, "CvBasalArea", + match, "CvPrimaryLayerSmall", mapEquals(expected.getCvPrimaryLayerSmall(), VdypMatchers::closeTo), item.getCvPrimaryLayerSmall(), mismatchDescription diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 731e21943..0870b082b 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -23,7 +24,11 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypSite; +import ca.bc.gov.nrs.vdyp.model.VolumeVariable; class VdypMatchersTest { @@ -39,17 +44,117 @@ static void assertMismatch(T item, Matcher unit, Matcher descript static void assertMatch(T item, Matcher unit) { var result = unit.matches(item); - if (result) { - return; - } var description = new StringDescription(); unit.describeMismatch(item, description); - assertFalse(result, "Expected match to pass but it failed with description: " + description.toString()); + assertTrue(result, "Expected match to pass but it failed with description: " + description.toString()); } @Nested class deepEquals { + + @Nested + class testVdypCompatibilityVariables { + VdypCompatibilityVariables expected; + Matcher unit; + + @BeforeEach + void setup() { + // Use a fixed seed so the random numbers are the same across runs. + Random rand = new Random(42); + + expected = VdypCompatibilityVariables.build(ib -> { + ib.polygonIdentifier("Test", 2024); + ib.layerType(LayerType.PRIMARY); + ib.genus("MB"); + ib.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + ib.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + ib.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + ib.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + unit = VdypMatchers.deepEquals(expected); + } + + @Test + void testPass() { + var actual = VdypCompatibilityVariables.build(ib -> { + ib.copy(expected); + }); + + assertMatch(actual, unit); + } + + @Test + void testOneVolumeEntryDifferent() { + var actual = VdypCompatibilityVariables.build(ib -> { + ib.copy(expected); + }); + + actual.getCvVolume().put( + UtilizationClass.U125TO175, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, 20f + ); + + assertMismatch( + actual, unit, matchesRegex( + "CvVolume at \\[, , \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" + ) + ); + } + + @Test + void testOneBaEntryDifferent() { + var actual = VdypCompatibilityVariables.build(ib -> { + ib.copy(expected); + }); + + actual.getCvBasalArea().put( + UtilizationClass.U125TO175, LayerType.PRIMARY, 20f + ); + + assertMismatch( + actual, unit, matchesRegex( + "CvBasalArea at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" + ) + ); + } + + @Test + void testOneDqEntryDifferent() { + var actual = VdypCompatibilityVariables.build(ib -> { + ib.copy(expected); + }); + + actual.getCvQuadraticMeanDiameter().put( + UtilizationClass.U125TO175, LayerType.PRIMARY, 20f + ); + + assertMismatch( + actual, unit, matchesRegex( + "CvQuadraticMeanDiameter at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" + ) + ); + } + + @Test + void testOneSmallEntryDifferent() { + var actual = VdypCompatibilityVariables.build(ib -> { + ib.copy(expected); + }); + + actual.getCvPrimaryLayerSmall().put( + UtilizationClassVariable.BASAL_AREA, 20f + ); + + assertMismatch( + actual, unit, matchesRegex( + "CvPrimaryLayerSmall at expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" + ) + ); + } + + } + @Nested class testVdypSite { VdypSite expected; From e107666c8a66e1e92ed22ac2186a1762fca9eb88 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 14 Nov 2024 11:21:45 -0800 Subject: [PATCH 28/45] Unit tests for deep equals matcher for species --- .../gov/nrs/vdyp/model/BaseVdypSpecies.java | 9 +- .../ca/bc/gov/nrs/vdyp/model/VdypSpecies.java | 41 ++- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 10 +- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 304 ++++++++++++++++-- 4 files changed, 334 insertions(+), 30 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java index 40380e11d..a093c86fd 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypSpecies.java @@ -290,8 +290,8 @@ protected String getBuilderId() { public Builder adapt(BaseVdypSpecies source) { polygonIdentifier(source.getPolygonIdentifier()); layerType(source.getLayerType()); - this.genus(source.getGenus()); - this.genus(source.getGenusIndex()); + genus(source.getGenus()); + genus(source.getGenusIndex()); percentGenus(source.getPercentGenus()); fractionGenus(source.getFractionGenus()); @@ -333,6 +333,11 @@ public Builder copySiteFrom(T specToCopy, BiConsumer config) { return this; } + public Builder copySp64DistributionFrom(T specToCopy) { + this.sp64DistributionSet(specToCopy.getSp64DistributionSet()); + return this; + } + protected abstract I buildSite(Consumer config); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java index 5b7f85d74..8f4cf7b0e 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java @@ -5,10 +5,12 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.function.BiConsumer; import java.util.function.Consumer; import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies.Builder; public class VdypSpecies extends BaseVdypSpecies implements VdypUtilizationHolder { @@ -194,6 +196,11 @@ public void setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization; } + public void setCompatibilityVariables(VdypCompatibilityVariables cv) { + this.compatibilityVariables = Optional.of(cv); + + } + public void setCompatibilityVariables( MatrixMap3 cvVolume, MatrixMap2 cvBasalArea, @@ -201,7 +208,7 @@ public void setCompatibilityVariables( Map cvPrimaryLayerSmall ) { - this.compatibilityVariables = Optional.of(VdypCompatibilityVariables.build(this, cvb -> { + this.setCompatibilityVariables(VdypCompatibilityVariables.build(this, cvb -> { cvb.cvVolume(cvVolume); cvb.cvBasalArea(cvBasalArea); @@ -334,8 +341,8 @@ public void closeUtilizationVolumeNetOfDecayAndWasteByUtilization(UtilizationVec protected UtilizationVector closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = VdypUtilizationHolder .emptyUtilization(); - private Optional> compatibilityVariablesBuilder; - private Optional compatibilityVariables; + private Optional> compatibilityVariablesBuilder = Optional.empty(); + private Optional compatibilityVariables = Optional.empty(); @Override public void closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(UtilizationVector vector) { @@ -400,7 +407,8 @@ protected void postProcess(VdypSpecies spec) { spec.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization ); - ; + + compatibilityVariables.ifPresent(spec::setCompatibilityVariables); } @Override @@ -459,14 +467,31 @@ public void addCompatibilityVariables(Optional cvs) this.compatibilityVariables = cvs; } + public void copyCompatibilityVariables( + VdypCompatibilityVariables source, + BiConsumer config + ) { + this.addCompatibilityVariables(builder -> { + builder.copy(source); + config.accept(builder, source); + }); + } + + public void copyCompatibilityVariablesFrom( + VdypSpecies specToCopy, + BiConsumer config + ) { + specToCopy.getCompatibilityVariables().ifPresent(source -> this.copyCompatibilityVariables(source, config)); + } + @Override protected void preProcess() { super.preProcess(); - compatibilityVariables = compatibilityVariablesBuilder.map(this::buildCompatibilityVariables).or( - () -> compatibilityVariables - ); - + compatibilityVariables = compatibilityVariablesBuilder + .map(this::buildCompatibilityVariables) + .or(() -> compatibilityVariables); + compatibilityVariablesBuilder = Optional.empty(); } protected VdypCompatibilityVariables buildCompatibilityVariables( diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index e64095ed7..4775de093 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -1122,7 +1122,7 @@ protected boolean matchesSafely(VdypSpecies item, Description mismatchDescriptio mismatchDescription.appendText("expected to have Compatibility Variables but none were present"); } - Utils.flatMapBoth( + match = match && Utils.flatMapBoth( expected.getCompatibilityVariables(), item.getCompatibilityVariables(), ( expectedCv, itemCv ) -> { @@ -1138,8 +1138,14 @@ protected boolean matchesSafely(VdypSpecies item, Description mismatchDescriptio return Optional.empty(); } + ).orElse(true); + + match = matchValue( + match, "Sp64DistributionSet", + expected.getSp64DistributionSet(), + item.getSp64DistributionSet(), + mismatchDescription ); - expected.getSp64DistributionSet(); return match; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 0870b082b..4a793a7ca 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -3,6 +3,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.matchesRegex; +import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -26,8 +27,10 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypSite; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; class VdypMatchersTest { @@ -50,9 +53,273 @@ static void assertMatch(T item, Matcher unit) { assertTrue(result, "Expected match to pass but it failed with description: " + description.toString()); } + Random rand = new Random(42); + + UtilizationVector mockUtilVector(float multiplier) { + return Utils.utilizationVector( + rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier + ); + } + + UtilizationVector mockHeightVector() { + return Utils.heightVector( + rand.nextFloat() * 5, + rand.nextFloat() * 20 + ); + } + @Nested class deepEquals { + @Nested + class testVdypSpecies { + VdypSpecies expected; + Matcher unit; + + @BeforeEach + void setup() { + Random rand = new Random(42); + var controlMap = TestUtils.loadControlMap(); + + // The numbers don't add up, we are just using them to test comparison + + expected = VdypSpecies.build(sb -> { + sb.polygonIdentifier("Test", 2024); + sb.layerType(LayerType.PRIMARY); + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + unit = VdypMatchers.deepEquals(expected); + } + + @Test + void testPass() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (ib, cv) -> { + }); + }); + + assertMatch(actual, unit); + } + + // Changing the key properties also causes mismatches on the children that share those key properties so use startsWith + + @Test + void testPolyIdDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.polygonIdentifier("Different", 2025); + }); + + assertMismatch( + actual, unit, startsWith( + "PolygonIdentifier was but expected " + ) + ); + } + + @Test + void testLayerDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.layerType(LayerType.VETERAN); + }); + + assertMismatch( + actual, unit, startsWith( + "LayerType was but expected " + ) + ); + } + + @Test + void testSpeciesGroupDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.genus("S"); + }); + + assertMismatch( + actual, unit, startsWith( + "Genus was \"S\" but expected \"MB\"" + ) + ); + } + + @Test + void testPercentDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.percentGenus(89); + }); + + assertMismatch( + actual, unit, equalTo( + "PercentGenus was <89.0F> but expected <90.0F>" + ) + ); + } + + @Test + void testBreakageGroupDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.breakageGroup(22); + }); + + assertMismatch( + actual, unit, equalTo( + "BreakageGroup was <22> but expected <12>" + ) + ); + } + + @Test + void testDecayGroupDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.decayGroup(23); + }); + + assertMismatch( + actual, unit, equalTo( + "DecayGroup was <23> but expected <13>" + ) + ); + } + + @Test + void testVolumeGroupDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + + sb.volumeGroup(24); + }); + + assertMismatch( + actual, unit, equalTo( + "VolumeGroup was <24> but expected <14>" + ) + ); + } + + @Test + void testSp64DistributionDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + sb.sp64DistributionList(List.of()); + sb.addSp64Distribution("S", 70); + sb.addSp64Distribution("F", 30); + }); + + assertMismatch( + actual, unit, equalTo( + "Sp64DistributionSet was <[S[1]:70.0, F[2]:30.0]> but expected <[MB[1]:100.0]>" + ) + ); + } + + @Test + void testCompatibilityVariablesDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + cvb.cvBasalArea((k1, k2) -> cv.getCvBasalArea(k1, k2) + 1); + }); + }); + + assertMismatch( + actual, unit, Matchers.matchesRegex( + "mismatch in Compatibility Variables \"MB\": CvBasalArea at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<\\d\\.\\d+F>\\) and there were \\d+ other mismatches" + ) + ); + } + + } + @Nested class testVdypCompatibilityVariables { VdypCompatibilityVariables expected; @@ -63,14 +330,14 @@ void setup() { // Use a fixed seed so the random numbers are the same across runs. Random rand = new Random(42); - expected = VdypCompatibilityVariables.build(ib -> { - ib.polygonIdentifier("Test", 2024); - ib.layerType(LayerType.PRIMARY); - ib.genus("MB"); - ib.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); - ib.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); - ib.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); - ib.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + expected = VdypCompatibilityVariables.build(cvb -> { + cvb.polygonIdentifier("Test", 2024); + cvb.layerType(LayerType.PRIMARY); + cvb.genus("MB"); + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); }); unit = VdypMatchers.deepEquals(expected); @@ -78,8 +345,8 @@ void setup() { @Test void testPass() { - var actual = VdypCompatibilityVariables.build(ib -> { - ib.copy(expected); + var actual = VdypCompatibilityVariables.build(cvb -> { + cvb.copy(expected); }); assertMatch(actual, unit); @@ -87,8 +354,8 @@ void testPass() { @Test void testOneVolumeEntryDifferent() { - var actual = VdypCompatibilityVariables.build(ib -> { - ib.copy(expected); + var actual = VdypCompatibilityVariables.build(cvb -> { + cvb.copy(expected); }); actual.getCvVolume().put( @@ -104,8 +371,8 @@ actual, unit, matchesRegex( @Test void testOneBaEntryDifferent() { - var actual = VdypCompatibilityVariables.build(ib -> { - ib.copy(expected); + var actual = VdypCompatibilityVariables.build(cvb -> { + cvb.copy(expected); }); actual.getCvBasalArea().put( @@ -121,8 +388,8 @@ actual, unit, matchesRegex( @Test void testOneDqEntryDifferent() { - var actual = VdypCompatibilityVariables.build(ib -> { - ib.copy(expected); + var actual = VdypCompatibilityVariables.build(cvb -> { + cvb.copy(expected); }); actual.getCvQuadraticMeanDiameter().put( @@ -138,8 +405,8 @@ actual, unit, matchesRegex( @Test void testOneSmallEntryDifferent() { - var actual = VdypCompatibilityVariables.build(ib -> { - ib.copy(expected); + var actual = VdypCompatibilityVariables.build(cvb -> { + cvb.copy(expected); }); actual.getCvPrimaryLayerSmall().put( @@ -153,6 +420,7 @@ actual, unit, matchesRegex( ); } + } @Nested From c603ec627a42c481943cfe68003fc400c932e8be Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 14 Nov 2024 17:11:32 -0800 Subject: [PATCH 29/45] Species deepEquals unit tests and generalization of *Variable enums as a replacement for reflective property access --- .../bc/gov/nrs/vdyp/model/VolumeVariable.java | 38 ++++++++- .../nrs/vdyp/model/variables/Property.java | 23 +++++ .../variables/UtilizationClassVariable.java | 84 +++++++++++++++++++ .../nrs/vdyp/model/variables/Variable.java | 10 +++ .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 20 +++++ .../gov/nrs/vdyp/test/VdypMatchersTest.java | 50 ++++++++++- 6 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java create mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java index e0183e1c1..8c050aeb1 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java @@ -2,11 +2,45 @@ import java.util.Collections; import java.util.List; +import java.util.function.Function; public enum VolumeVariable { - WHOLE_STEM_VOL, CLOSE_UTIL_VOL, CLOSE_UTIL_VOL_LESS_DECAY, CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE; + WHOLE_STEM_VOL("WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization), + CLOSE_UTIL_VOL("CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization), + CLOSE_UTIL_VOL_LESS_DECAY( + "CloseUtilizationVolumeNetOfDecay", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization + ), + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE( + "CloseUtilizationVolumeNetOfDecayAndWaste", + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization + ), + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE( + "CloseUtilizationVolumeNetOfDecayWasteAndBreakage", + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization + ); + + private VolumeVariable(String name, Function accessor) { + this.name = name; + this.accessor = accessor; + } + public final Function accessor; + public final String name; + + public static final List ALL_BUT_NET_OF_BREAKAGE = Collections.unmodifiableList( + List.of( + WHOLE_STEM_VOL, + CLOSE_UTIL_VOL, + CLOSE_UTIL_VOL_LESS_DECAY, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE) + ); public static final List ALL = Collections.unmodifiableList( - List.of(WHOLE_STEM_VOL, CLOSE_UTIL_VOL, CLOSE_UTIL_VOL_LESS_DECAY, CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE) + List.of( + WHOLE_STEM_VOL, + CLOSE_UTIL_VOL, + CLOSE_UTIL_VOL_LESS_DECAY, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE + ) ); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java new file mode 100644 index 000000000..ba733e0bd --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java @@ -0,0 +1,23 @@ +package ca.bc.gov.nrs.vdyp.model.variables; + +/** + * Represents a property of a model object in a reflection-like way without using reflection + * @param

+ * @param + */ +public interface Property { + + /** + * Get the name of the property + * @return + */ + String getName(); + + /** + * Get the value of the property from a particular parent object + * @param parent + * @return + */ + T get(P parent); + +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java new file mode 100644 index 000000000..9e4797e72 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java @@ -0,0 +1,84 @@ +package ca.bc.gov.nrs.vdyp.model.variables; + +import java.util.EnumSet; +import java.util.Set; +import java.util.function.Function; + +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; + +/** + * Represents fields that have a UtilizationVector as a type + */ +public enum UtilizationClassVariable implements Property { + LOREY_HEIGHT("LoreyHeight", VdypUtilizationHolder::getLoreyHeightByUtilization) { + // Lorey Height only has 2 classes instead of the usual 6 + @Override + public Set getClasses() { + return HEIGHT_CLASSES; + } + }, + BASAL_AREA("BaseArea", VdypUtilizationHolder::getBaseAreaByUtilization), + QUAD_MEAN_DIAMETER("QuadraticMeanDiameter", VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization), + TREES_PER_HECTARE("TreesPerHectare", VdypUtilizationHolder::getTreesPerHectareByUtilization), + + WHOLE_STEM_VOL("WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization), + CLOSE_UTIL_VOL("CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization), + CLOSE_UTIL_VOL_LESS_DECAY( + "CloseUtilizationVolumeNetOfDecay", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization + ), + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE( + "CloseUtilizationVolumeNetOfDecayAndWaste", + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization + ), + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE( + "CloseUtilizationVolumeNetOfDecayWasteAndBreakage", + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization + ) + ; + + static private final Set STANDARD_CLASSES = EnumSet.allOf(UtilizationClass.class); + static private final Set HEIGHT_CLASSES = EnumSet + .of(UtilizationClass.SMALL, UtilizationClass.ALL); + + + + private final String shortName; + private final String longName; + private final Function getter; + + private UtilizationClassVariable(String shortName, Function accessor) { + this.shortName = shortName; + // Pre-computing it at init instead of using the default to improve performance + this.longName = shortName + "ByUtilization"; + this.getter = accessor; + } + + @Override + public UtilizationVector get(VdypUtilizationHolder parent) { + return getter.apply(parent); + } + + /** + * Short name of the property without "ByUtilization" + * @return + */ + public String getShortName() { + return shortName; + } + + @Override + public String getName() { + return longName; + } + + /** + * The UtilizationClasses this field covers + * + * @return + */ + public Set getClasses() { + return STANDARD_CLASSES; + } +} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java new file mode 100644 index 000000000..9a19855f9 --- /dev/null +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java @@ -0,0 +1,10 @@ +package ca.bc.gov.nrs.vdyp.model.variables; + +public interface Variable extends Property { + /** + * Set the value of a variable field on a particular parent + * @param parent + * @param value + */ + void set(P parent, T value); +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 1d18ecc74..b6ed4ff97 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -18,6 +18,8 @@ import java.io.OutputStream; import java.io.UncheckedIOException; import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -73,6 +75,7 @@ import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; import ca.bc.gov.nrs.vdyp.model.VolumeVariable; public class TestUtils { @@ -1091,4 +1094,21 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { } } + /** + * Get the utilization vector for a specified field + * @param holder VdypUtilizationHolder + * @param property The field to get, do not include "ByUtilization" + * @return The UtilizationVector for the named field + * @throws NoSuchMethodException + * @throws SecurityException + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + */ + public static UtilizationVector getUtilization(VdypUtilizationHolder holder, String property) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + var accessor = VdypUtilizationHolder.class.getMethod("get"+property+"ByUtilization"); + var vector = (UtilizationVector) accessor.invoke(holder); + return vector; + } + } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 4a793a7ca..d5b5c6f74 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -18,6 +18,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.LayerType; @@ -71,7 +73,7 @@ UtilizationVector mockHeightVector() { rand.nextFloat() * 20 ); } - + @Nested class deepEquals { @@ -312,12 +314,56 @@ void testCompatibilityVariablesDifferent() { }); assertMismatch( - actual, unit, Matchers.matchesRegex( + actual, unit, matchesRegex( "mismatch in Compatibility Variables \"MB\": CvBasalArea at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<\\d\\.\\d+F>\\) and there were \\d+ other mismatches" ) ); } + @Test + void testSiteDifferent() { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + ib.height(22); + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + }); + + assertMismatch( + actual, unit, equalTo( + "mismatch in site \"MB\": Height was but expected " + ) + ); + } + + @ParameterizedTest + @ValueSource(strings={"LoreyHeight", "BaseArea", "QuadraticMeanDiameter", "TreesPerHectare"}) + void testUtilizationDifferent(String property) throws Exception { + var actual = VdypSpecies.build(sb -> { + sb.copy(expected); + sb.copySiteFrom(expected, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expected, (cvb, cv) -> { + }); + }); + + // Make a change to one entry (SMALL) in the utilization vector for the specified field + + TestUtils.getUtilization(actual, property).scalarInPlace(UtilizationClass.SMALL, x->x+1); + + assertMismatch( + actual, unit, equalTo( + "" + ) + ); + + + } + + + } @Nested From 8344baabe4da73db103bdbe303f418bf1300ac08 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 12:00:59 -0800 Subject: [PATCH 30/45] Refactor to use common enum for UC variables --- .../nrs/vdyp/back/BackProcessingEngine.java | 8 +- .../processing_state/BackProcessingState.java | 10 +- .../vdyp/back/BackProcessingEngineTest.java | 13 +- .../nrs/vdyp/common/ComputationMethods.java | 30 +- .../java/ca/bc/gov/nrs/vdyp/common/Utils.java | 29 +- .../nrs/vdyp/model/CompVarAdjustments.java | 59 +-- .../model/SmallUtilizationClassVariable.java | 5 - .../vdyp/model/UtilizationClassVariable.java | 11 - .../model/VdypCompatibilityVariables.java | 45 ++- .../ca/bc/gov/nrs/vdyp/model/VdypSpecies.java | 11 +- .../bc/gov/nrs/vdyp/model/VolumeVariable.java | 46 --- .../nrs/vdyp/model/variables/Property.java | 7 +- .../variables/UtilizationClassVariable.java | 10 +- .../nrs/vdyp/model/variables/Variable.java | 1 + .../LayerProcessingState.java | 12 +- .../parse/CompVarAdjustmentsParserTest.java | 61 ++-- .../gov/nrs/vdyp/model/VdypSpeciesTest.java | 6 +- .../LayerProcessingStateTest.java | 50 ++- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 161 +++------ .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 335 ++++++------------ .../gov/nrs/vdyp/test/VdypMatchersTest.java | 232 +++++------- .../vdyp/forward/ForwardProcessingEngine.java | 27 +- .../vdyp/forward/LayerProcessingState.java | 17 +- .../forward/ForwardControlParserTest.java | 2 +- .../Grow11UpdateCompatibilityVariables.java | 12 +- ...liminarySetCompatibilityVariablesTest.java | 25 +- 26 files changed, 509 insertions(+), 716 deletions(-) delete mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/SmallUtilizationClassVariable.java delete mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClassVariable.java delete mode 100644 lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java index 88cf79674..ba1a08b1f 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -18,9 +18,9 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; public class BackProcessingEngine extends ProcessingEngine { @@ -66,7 +66,7 @@ void prepare(BackProcessingState state) throws ProcessingException { int specCount = primaryState.getNSpecies(); @SuppressWarnings("unchecked") - MatrixMap2[] cvVolume = new MatrixMap2[specCount + 1]; + MatrixMap2[] cvVolume = new MatrixMap2[specCount + 1]; @SuppressWarnings("unchecked") Map[] cvBasalArea = new Map[specCount + 1]; @SuppressWarnings("unchecked") @@ -77,7 +77,7 @@ void prepare(BackProcessingState state) throws ProcessingException { for (int i = 0; i < primaryState.getNSpecies(); i++) { final int specIndex = i + 1; cvVolume[specIndex] = new MatrixMap2Impl<>( - List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), + List.of(UtilizationClass.values()), VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, (uc, vv) -> primaryState.getCVVolume(specIndex, uc, vv, LayerType.PRIMARY) ); diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java index f09a3e9ef..a5976a254 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java @@ -11,10 +11,9 @@ import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; public class BackProcessingState extends ProcessingState { @@ -32,7 +31,7 @@ public class BackProcessingState extends ProcessingState[] cvVolume; + private MatrixMap2[] cvVolume; private Map[] cvBasalArea; private Map[] cvQuadraticMeanDiameter; private Map[] cvPrimaryLayerSmall; @@ -69,7 +68,8 @@ public Optional getBaseAreaVeteran() { } public void setCompatibilityVariableDetails( - MatrixMap2[] cvVolume, Map[] cvBasalArea, + MatrixMap2[] cvVolume, + Map[] cvBasalArea, Map[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall ) { @@ -85,7 +85,7 @@ public void setCompatibilityVariableDetails( areCompatibilityVariablesSet = true; } - public float getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable) { + public float getCVVolume(int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable) { if (!areCompatibilityVariablesSet) { throw UNSET_CV_VOLUMES.get(); } diff --git a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java index 8527dee2f..a52ec5cea 100644 --- a/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java +++ b/lib/vdyp-back/src/test/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngineTest.java @@ -31,9 +31,9 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; class BackProcessingEngineTest { @@ -94,9 +94,10 @@ public BackProcessingState primaryOnlyWithSingleSpecies() throws ProcessingExcep state.setPolygon(polygon); @SuppressWarnings("unchecked") - MatrixMap3[] cvVolume = new MatrixMap3[] { null, - new MatrixMap3Impl( - List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), + MatrixMap3[] cvVolume = new MatrixMap3[] { + null, + new MatrixMap3Impl( + List.of(UtilizationClass.values()), VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, List.of(LayerType.values()), (uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f ) }; @@ -118,7 +119,7 @@ public BackProcessingState primaryOnlyWithSingleSpecies() throws ProcessingExcep Map[] cvSm = new EnumMap[] { null, new EnumMap(UtilizationClassVariable.class) }; - for (var uc : UtilizationClassVariable.values()) { + for (var uc : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { cvSm[1].put(uc, uc.ordinal() * 7f); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java index 51668b038..4c127b9ad 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java @@ -26,7 +26,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class ComputationMethods { @@ -166,7 +166,9 @@ public void computeUtilizationComponentsPrimary( for (var uc : VdypStartApplication.UTIL_CLASSES) { float currentUcBasalArea = basalAreaUtil.get(uc); - basalAreaUtil.set(uc, currentUcBasalArea + compatibilityVariables.getCvBasalArea(uc, spec.getLayerType())); + basalAreaUtil.set( + uc, currentUcBasalArea + compatibilityVariables.getCvBasalArea(uc, spec.getLayerType()) + ); if (basalAreaUtil.get(uc) < 0.0f) { basalAreaUtil.set(uc, 0.0f); } @@ -221,8 +223,11 @@ public void computeUtilizationComponentsPrimary( for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { wholeStemVolumeUtil.set( uc, - wholeStemVolumeUtil.get(uc) * FloatMath - .exp(compatibilityVariables.getCvVolume(uc, VolumeVariable.WHOLE_STEM_VOL, spec.getLayerType())) + wholeStemVolumeUtil.get(uc) * FloatMath.exp( + compatibilityVariables.getCvVolume( + uc, UtilizationClassVariable.WHOLE_STEM_VOL, spec.getLayerType() + ) + ) ); wholeStemVolumeSum += wholeStemVolumeUtil.get(uc); } @@ -230,14 +235,23 @@ public void computeUtilizationComponentsPrimary( // Set the adjustment factors for next three volume types for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - adjustCloseUtil - .set(uc, compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL, spec.getLayerType())); + adjustCloseUtil.set( + uc, + compatibilityVariables + .getCvVolume(uc, UtilizationClassVariable.CLOSE_UTIL_VOL, spec.getLayerType()) + ); adjustDecayUtil.set( - uc, compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY, spec.getLayerType()) + uc, + compatibilityVariables.getCvVolume( + uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, spec.getLayerType() + ) ); adjustDecayWasteUtil.set( uc, - compatibilityVariables.getCvVolume(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, spec.getLayerType()) + compatibilityVariables.getCvVolume( + uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + spec.getLayerType() + ) ); } } else { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java index 7994e9a78..f3df8151f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -16,6 +17,7 @@ import java.util.function.Consumer; import java.util.function.DoubleUnaryOperator; import java.util.function.Function; +import java.util.function.Supplier; import javax.annotation.Nullable; @@ -215,7 +217,32 @@ public static > int compareOptionals(Optional t1, Opt * @return */ public static Map constMap(Consumer> body) { - var map = new HashMap(); + return constMap(HashMap::new, body); + } + + /** + * Create a map, allow it to be modified, then return an unmodifiable view of it. + * + * @param + * @param + * @param body + * @return + */ + public static , V> Map constMap(Class keyEnum, Consumer> body) { + return constMap(() -> new EnumMap(keyEnum), body); + } + + /** + * Create map, allow it to be modified, then return an unmodifiable view of it. + * + * @param + * @param + * @param body + * @param constructor + * @return + */ + public static Map constMap(Supplier> constructor, Consumer> body) { + var map = constructor.get(); body.accept(map); return Collections.unmodifiableMap(map); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/CompVarAdjustments.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/CompVarAdjustments.java index 217d424d4..d8f8e4845 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/CompVarAdjustments.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/CompVarAdjustments.java @@ -4,6 +4,9 @@ import java.util.HashMap; import java.util.Map; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; + public class CompVarAdjustments { public static final int MIN_INDEX = 1; public static final int MAX_INDEX = 98; @@ -19,29 +22,35 @@ public class CompVarAdjustments { private final Map smallUtilizationClassVariables = new HashMap<>(); private final Map utilizationClassBasalAreaVariables = new HashMap<>(); private final Map utilizationClassQuadMeanDiameterVariables = new HashMap<>(); - private final MatrixMap2 utilizationClassVolumeVariables = new MatrixMap2Impl<>( - VolumeVariable.ALL, UtilizationClass.UTIL_CLASSES, (k1, k2) -> 1.0f + private final MatrixMap2 utilizationClassVolumeVariables = new MatrixMap2Impl<>( + VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, UtilizationClass.UTIL_CLASSES, (k1, k2) -> 1.0f ); private float loreyHeightPrimary; private float loreyHeightOther; - static { - assert UtilizationClassVariable.BASAL_AREA.ordinal() == 0; - assert UtilizationClassVariable.QUAD_MEAN_DIAMETER.ordinal() == 1; - assert UtilizationClassVariable.LOREY_HEIGHT.ordinal() == 2; - assert UtilizationClassVariable.WHOLE_STEM_VOLUME.ordinal() == 3; - - assert UtilizationClass.U75TO125.ordinal() == 2; - assert UtilizationClass.U125TO175.ordinal() == 3; - assert UtilizationClass.U175TO225.ordinal() == 4; - assert UtilizationClass.OVER225.ordinal() == 5; - - assert VolumeVariable.WHOLE_STEM_VOL.ordinal() == 0; - assert VolumeVariable.CLOSE_UTIL_VOL.ordinal() == 1; - assert VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY.ordinal() == 2; - assert VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE.ordinal() == 3; - } + private static final Map SMALL_INDEX = Utils + .constMap(UtilizationClassVariable.class, map -> { + map.put(UtilizationClassVariable.BASAL_AREA, 0); + map.put(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 1); + map.put(UtilizationClassVariable.LOREY_HEIGHT, 2); + map.put(UtilizationClassVariable.WHOLE_STEM_VOL, 3); + }); + + private static final Map CLASS_INDEX = Utils.constMap(UtilizationClass.class, map -> { + map.put(UtilizationClass.U75TO125, 0); + map.put(UtilizationClass.U125TO175, 1); + map.put(UtilizationClass.U175TO225, 2); + map.put(UtilizationClass.OVER225, 3); + }); + + private static final Map VOLUME_INDEX = Utils + .constMap(UtilizationClassVariable.class, map -> { + map.put(UtilizationClassVariable.WHOLE_STEM_VOL, 0); + map.put(UtilizationClassVariable.CLOSE_UTIL_VOL, 1); + map.put(UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, 2); + map.put(UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, 3); + }); private final static Map ucOffsets = new HashMap<>(); private final static Map defaultValuesMap = new HashMap<>(); @@ -68,21 +77,21 @@ public CompVarAdjustments() { */ public CompVarAdjustments(Map values) { - for (UtilizationClassVariable ucv : UtilizationClassVariable.values()) { - smallUtilizationClassVariables.put(ucv, values.get(SMALL_VARIABLE_START_INDEX + ucv.ordinal())); + for (UtilizationClassVariable ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { + smallUtilizationClassVariables.put(ucv, values.get(SMALL_VARIABLE_START_INDEX + SMALL_INDEX.get(ucv))); } for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - utilizationClassBasalAreaVariables.put(uc, values.get(BA_ADJ_UC_START_INDEX + uc.ordinal() - 2)); + utilizationClassBasalAreaVariables.put(uc, values.get(BA_ADJ_UC_START_INDEX + CLASS_INDEX.get(uc))); } for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - utilizationClassQuadMeanDiameterVariables.put(uc, values.get(DQ_ADJ_UC_START_INDEX + uc.ordinal() - 2)); + utilizationClassQuadMeanDiameterVariables.put(uc, values.get(DQ_ADJ_UC_START_INDEX + CLASS_INDEX.get(uc))); } - for (VolumeVariable vv : VolumeVariable.ALL) { + for (UtilizationClassVariable vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - utilizationClassVolumeVariables.put(vv, uc, values.get(ucOffsets.get(uc) + vv.ordinal())); + utilizationClassVolumeVariables.put(vv, uc, values.get(ucOffsets.get(uc) + VOLUME_INDEX.get(vv))); } } @@ -120,7 +129,7 @@ public float getValue(UtilizationClass uc, UtilizationClassVariable v) { ); } - public float getVolumeValue(UtilizationClass uc, VolumeVariable vv) { + public float getVolumeValue(UtilizationClass uc, UtilizationClassVariable vv) { if (uc.ordinal() >= UtilizationClass.U75TO125.ordinal()) { return utilizationClassVolumeVariables.get(vv, uc); } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/SmallUtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/SmallUtilizationClassVariable.java deleted file mode 100644 index 798087f7d..000000000 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/SmallUtilizationClassVariable.java +++ /dev/null @@ -1,5 +0,0 @@ -package ca.bc.gov.nrs.vdyp.model; - -public enum SmallUtilizationClassVariable { - BASAL_AREA, QUAD_MEAN_DIAMETER, LOREY_HEIGHT, WHOLE_STEM_VOLUME; -} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClassVariable.java deleted file mode 100644 index c3642057e..000000000 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClassVariable.java +++ /dev/null @@ -1,11 +0,0 @@ -package ca.bc.gov.nrs.vdyp.model; - -import java.util.Collections; -import java.util.List; - -public enum UtilizationClassVariable { - BASAL_AREA, QUAD_MEAN_DIAMETER, LOREY_HEIGHT, WHOLE_STEM_VOLUME; - - public static final List ALL = Collections - .unmodifiableList(List.of(BASAL_AREA, QUAD_MEAN_DIAMETER, LOREY_HEIGHT, WHOLE_STEM_VOLUME)); -} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java index 79385f954..7acd0c43b 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypCompatibilityVariables.java @@ -2,9 +2,11 @@ import java.text.MessageFormat; import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -12,16 +14,27 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl.TriFunction; import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.builders.SpeciesGroupIdentifiedBuilder; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class VdypCompatibilityVariables { - private final MatrixMap3 cvVolume; + private final MatrixMap3 cvVolume; private final MatrixMap2 cvBasalArea; private final MatrixMap2 cvQuadraticMeanDiameter; private final Map cvPrimaryLayerSmall; + public static final Set VOLUME_UTILIZATION_VARIABLES = EnumSet.of( + UtilizationClassVariable.WHOLE_STEM_VOL, UtilizationClassVariable.CLOSE_UTIL_VOL, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE + ); + public static final Set SMALL_UTILIZATION_VARIABLES = EnumSet.of( + UtilizationClassVariable.BASAL_AREA, UtilizationClassVariable.QUAD_MEAN_DIAMETER, + UtilizationClassVariable.LOREY_HEIGHT, UtilizationClassVariable.WHOLE_STEM_VOL + ); + public VdypCompatibilityVariables( - MatrixMap3 cvVolume, + MatrixMap3 cvVolume, MatrixMap2 cvBasalArea, MatrixMap2 cvQuadraticMeanDiameter, Map cvPrimaryLayerSmall @@ -33,7 +46,7 @@ public VdypCompatibilityVariables( this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; } - public MatrixMap3 getCvVolume() { + public MatrixMap3 getCvVolume() { return cvVolume; } @@ -49,7 +62,7 @@ public Map getCvPrimaryLayerSmall() { return cvPrimaryLayerSmall; } - public float getCvVolume(UtilizationClass uc, VolumeVariable vv, LayerType lt) { + public float getCvVolume(UtilizationClass uc, UtilizationClassVariable vv, LayerType lt) { return getCvVolume().get(uc, vv, lt); } @@ -107,30 +120,26 @@ public void polygonIndentifier(PolygonIdentifier polyId) { this.polygonIndentifier(polyId); } - private Optional> cvVolume = Optional.empty(); + private Optional> cvVolume = Optional + .empty(); private Optional> cvBasalArea = Optional.empty(); private Optional> cvQuadraticMeanDiameter = Optional.empty(); private Optional> cvPrimaryLayerSmall = Optional.empty(); - public void cvVolume(TriFunction init) { - MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( - UtilizationClass.UTIL_CLASSES, - VolumeVariable.ALL, - LayerType.ALL_USED, - init + public void cvVolume(TriFunction init) { + MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, init ); cvVolume(cvVolume); } - public void cvVolume(MatrixMap3 cvVolume) { + public void cvVolume(MatrixMap3 cvVolume) { this.cvVolume = Optional.of(cvVolume); } public void cvBasalArea(BiFunction init) { MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, - LayerType.ALL_USED, - init + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, init ); cvBasalArea(cvBasalArea); } @@ -141,9 +150,7 @@ public void cvBasalArea(MatrixMap2 cvBasalAr public void cvQuadraticMeanDiameter(BiFunction init) { MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, - LayerType.ALL_USED, - init + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, init ); cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); } @@ -154,7 +161,7 @@ public void cvQuadraticMeanDiameter(MatrixMap2 init) { var cvPrimaryLayerSmall = new HashMap(); - for (var ucv : UtilizationClassVariable.ALL) { + for (var ucv : SMALL_UTILIZATION_VARIABLES) { cvPrimaryLayerSmall.put(ucv, init.apply(ucv)); } cvPrimaryLayerSmall(cvPrimaryLayerSmall); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java index 8f4cf7b0e..54b2914ca 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java @@ -11,6 +11,7 @@ import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies.Builder; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class VdypSpecies extends BaseVdypSpecies implements VdypUtilizationHolder { @@ -202,7 +203,7 @@ public void setCompatibilityVariables(VdypCompatibilityVariables cv) { } public void setCompatibilityVariables( - MatrixMap3 cvVolume, + MatrixMap3 cvVolume, MatrixMap2 cvBasalArea, MatrixMap2 cvQuadraticMeanDiameter, Map cvPrimaryLayerSmall @@ -488,15 +489,13 @@ public void copyCompatibilityVariablesFrom( protected void preProcess() { super.preProcess(); - compatibilityVariables = compatibilityVariablesBuilder - .map(this::buildCompatibilityVariables) + compatibilityVariables = compatibilityVariablesBuilder.map(this::buildCompatibilityVariables) .or(() -> compatibilityVariables); compatibilityVariablesBuilder = Optional.empty(); } - protected VdypCompatibilityVariables buildCompatibilityVariables( - Consumer config - ) { + protected VdypCompatibilityVariables + buildCompatibilityVariables(Consumer config) { return VdypCompatibilityVariables.build(builder -> { config.accept(builder); builder.polygonIdentifier(polygonIdentifier.get()); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java deleted file mode 100644 index 8c050aeb1..000000000 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VolumeVariable.java +++ /dev/null @@ -1,46 +0,0 @@ -package ca.bc.gov.nrs.vdyp.model; - -import java.util.Collections; -import java.util.List; -import java.util.function.Function; - -public enum VolumeVariable { - WHOLE_STEM_VOL("WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization), - CLOSE_UTIL_VOL("CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization), - CLOSE_UTIL_VOL_LESS_DECAY( - "CloseUtilizationVolumeNetOfDecay", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization - ), - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE( - "CloseUtilizationVolumeNetOfDecayAndWaste", - VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization - ), - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE( - "CloseUtilizationVolumeNetOfDecayWasteAndBreakage", - VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization - ); - - private VolumeVariable(String name, Function accessor) { - this.name = name; - this.accessor = accessor; - } - - public final Function accessor; - public final String name; - - public static final List ALL_BUT_NET_OF_BREAKAGE = Collections.unmodifiableList( - List.of( - WHOLE_STEM_VOL, - CLOSE_UTIL_VOL, - CLOSE_UTIL_VOL_LESS_DECAY, - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE) - ); - public static final List ALL = Collections.unmodifiableList( - List.of( - WHOLE_STEM_VOL, - CLOSE_UTIL_VOL, - CLOSE_UTIL_VOL_LESS_DECAY, - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE - ) - ); -} diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java index ba733e0bd..3e8ecf8bd 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Property.java @@ -2,6 +2,7 @@ /** * Represents a property of a model object in a reflection-like way without using reflection + * * @param

* @param */ @@ -9,15 +10,17 @@ public interface Property { /** * Get the name of the property + * * @return */ String getName(); - + /** * Get the value of the property from a particular parent object + * * @param parent * @return */ T get(P parent); - + } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java index 9e4797e72..3597ebe4b 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java @@ -22,7 +22,7 @@ public Set getClasses() { BASAL_AREA("BaseArea", VdypUtilizationHolder::getBaseAreaByUtilization), QUAD_MEAN_DIAMETER("QuadraticMeanDiameter", VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization), TREES_PER_HECTARE("TreesPerHectare", VdypUtilizationHolder::getTreesPerHectareByUtilization), - + WHOLE_STEM_VOL("WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization), CLOSE_UTIL_VOL("CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization), CLOSE_UTIL_VOL_LESS_DECAY( @@ -35,15 +35,12 @@ public Set getClasses() { CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE( "CloseUtilizationVolumeNetOfDecayWasteAndBreakage", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization - ) - ; + ); static private final Set STANDARD_CLASSES = EnumSet.allOf(UtilizationClass.class); static private final Set HEIGHT_CLASSES = EnumSet .of(UtilizationClass.SMALL, UtilizationClass.ALL); - - private final String shortName; private final String longName; private final Function getter; @@ -62,6 +59,7 @@ public UtilizationVector get(VdypUtilizationHolder parent) { /** * Short name of the property without "ByUtilization" + * * @return */ public String getShortName() { @@ -75,7 +73,7 @@ public String getName() { /** * The UtilizationClasses this field covers - * + * * @return */ public Set getClasses() { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java index 9a19855f9..ba9ac96fe 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/Variable.java @@ -3,6 +3,7 @@ public interface Variable extends Property { /** * Set the value of a variable field on a particular parent + * * @param parent * @param value */ diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index 1ba0d8e4d..36027cebf 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -13,11 +13,10 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public abstract class LayerProcessingState> { @@ -47,7 +46,7 @@ public abstract class LayerProcessingState[] cvVolume; + private MatrixMap3[] cvVolume; private MatrixMap2[] cvBasalArea; private MatrixMap2[] cvQuadraticMeanDiameter; private Map[] cvPrimaryLayerSmall; @@ -100,7 +99,7 @@ public int getNSpecies() { protected abstract VdypLayer updateLayerFromBank(); public void setCompatibilityVariableDetails( - MatrixMap3[] cvVolume, + MatrixMap3[] cvVolume, MatrixMap2[] cvBasalArea, MatrixMap2[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall @@ -117,8 +116,9 @@ public void setCompatibilityVariableDetails( areCompatibilityVariablesSet = true; } - public float - getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable, LayerType layerType) { + public float getCVVolume( + int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable, LayerType layerType + ) { if (!areCompatibilityVariablesSet) { throw new IllegalStateException(UNSET_CV_VOLUMES); } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CompVarAdjustmentsParserTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CompVarAdjustmentsParserTest.java index 14d4ee032..26787ab43 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CompVarAdjustmentsParserTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CompVarAdjustmentsParserTest.java @@ -15,8 +15,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.coe.CompVarAdjustmentsParser; import ca.bc.gov.nrs.vdyp.model.CompVarAdjustments; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; /** @@ -24,7 +23,7 @@ * * @author Michael Junkin, Vivid Solutions */ -public class CompVarAdjustmentsParserTest { +class CompVarAdjustmentsParserTest { @Test void testParseSimple() throws Exception { @@ -74,7 +73,7 @@ void testParseSimple() throws Exception { assertThat(m.getValue(UtilizationClass.SMALL, UtilizationClassVariable.BASAL_AREA), is(0.50f)); assertThat(m.getValue(UtilizationClass.SMALL, UtilizationClassVariable.QUAD_MEAN_DIAMETER), is(0.51f)); assertThat(m.getValue(UtilizationClass.SMALL, UtilizationClassVariable.LOREY_HEIGHT), is(0.52f)); - assertThat(m.getValue(UtilizationClass.SMALL, UtilizationClassVariable.WHOLE_STEM_VOLUME), is(0.53f)); + assertThat(m.getValue(UtilizationClass.SMALL, UtilizationClassVariable.WHOLE_STEM_VOL), is(0.53f)); assertThat(m.getLoreyHeightPrimaryParam(), is(0.80f)); assertThat(m.getLoreyHeightOther(), is(0.81f)); @@ -94,43 +93,59 @@ void testParseSimple() throws Exception { ); assertThrows( IllegalArgumentException.class, - () -> m.getValue(UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOLUME) + () -> m.getValue(UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL) ); assertThrows( IllegalArgumentException.class, () -> m.getValue(UtilizationClass.ALL, UtilizationClassVariable.BASAL_AREA) ); - assertThat(m.getVolumeValue(UtilizationClass.U75TO125, VolumeVariable.WHOLE_STEM_VOL), is(0.91f)); - assertThat(m.getVolumeValue(UtilizationClass.U125TO175, VolumeVariable.WHOLE_STEM_VOL), is(0.92f)); - assertThat(m.getVolumeValue(UtilizationClass.U175TO225, VolumeVariable.WHOLE_STEM_VOL), is(0.93f)); - assertThat(m.getVolumeValue(UtilizationClass.OVER225, VolumeVariable.WHOLE_STEM_VOL), is(0.94f)); + assertThat(m.getVolumeValue(UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL), is(0.91f)); + assertThat(m.getVolumeValue(UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL), is(0.92f)); + assertThat(m.getVolumeValue(UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL), is(0.93f)); + assertThat(m.getVolumeValue(UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL), is(0.94f)); - assertThat(m.getVolumeValue(UtilizationClass.U75TO125, VolumeVariable.CLOSE_UTIL_VOL), is(0.92f)); - assertThat(m.getVolumeValue(UtilizationClass.U125TO175, VolumeVariable.CLOSE_UTIL_VOL), is(0.93f)); - assertThat(m.getVolumeValue(UtilizationClass.U175TO225, VolumeVariable.CLOSE_UTIL_VOL), is(0.94f)); - assertThat(m.getVolumeValue(UtilizationClass.OVER225, VolumeVariable.CLOSE_UTIL_VOL), is(0.95f)); - - assertThat(m.getVolumeValue(UtilizationClass.U75TO125, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.93f)); - assertThat(m.getVolumeValue(UtilizationClass.U125TO175, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.94f)); - assertThat(m.getVolumeValue(UtilizationClass.U175TO225, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.95f)); - assertThat(m.getVolumeValue(UtilizationClass.OVER225, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.96f)); + assertThat(m.getVolumeValue(UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL), is(0.92f)); + assertThat(m.getVolumeValue(UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL), is(0.93f)); + assertThat(m.getVolumeValue(UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL), is(0.94f)); + assertThat(m.getVolumeValue(UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL), is(0.95f)); assertThat( - m.getVolumeValue(UtilizationClass.U75TO125, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE), + m.getVolumeValue(UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY), + is(0.93f) + ); + assertThat( + m.getVolumeValue(UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.94f) ); assertThat( - m.getVolumeValue(UtilizationClass.U125TO175, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE), + m.getVolumeValue(UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.95f) ); assertThat( - m.getVolumeValue(UtilizationClass.U175TO225, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE), + m.getVolumeValue(UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY), is(0.96f) ); + + assertThat( + m.getVolumeValue( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE + ), is(0.94f) + ); + assertThat( + m.getVolumeValue( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE + ), is(0.95f) + ); + assertThat( + m.getVolumeValue( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE + ), is(0.96f) + ); assertThat( - m.getVolumeValue(UtilizationClass.OVER225, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE), - is(0.97f) + m.getVolumeValue( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE + ), is(0.97f) ); } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java index 533136462..5ab9ea720 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java @@ -16,10 +16,12 @@ import java.util.HashMap; import java.util.List; import java.util.NoSuchElementException; +import java.util.Set; import org.junit.jupiter.api.Test; import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.test.VdypMatchers; class VdypSpeciesTest { @@ -122,13 +124,13 @@ void testAdditionalBuildMethods() { assertThrows(IllegalStateException.class, () -> sp.setBreakageGroup(1)); List ucs = Arrays.asList(UtilizationClass.values()); - List vvs = Arrays.asList(VolumeVariable.values()); + Set vvs = VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES; List lts = Arrays.asList(LayerType.values()); assertThrows(InitializationIncompleteException.class, () -> sp.requireCompatibilityVariables()); assertThat(sp, hasProperty("compatibilityVariables", notPresent())); - var cvVolume = new MatrixMap3Impl( + var cvVolume = new MatrixMap3Impl( ucs, vvs, lts, (x, y, z) -> 1.0f ); var cvBasalArea = new MatrixMap2Impl(ucs, lts, (x, y) -> 1.0f); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java index 67b6ab71d..14e720395 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java @@ -27,11 +27,11 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; class LayerProcessingStateTest { @@ -131,7 +131,7 @@ class SetCompatibilityVariables { IMocksControl em; LayerProcessingState unit; - MatrixMap3[] cvVolume; + MatrixMap3[] cvVolume; MatrixMap2[] cvBa; MatrixMap2[] cvDq; Map[] cvSm; @@ -162,10 +162,12 @@ void setup() throws ProcessingException { unit = new TestLayerProcessingState(parent, polygon, LayerType.PRIMARY); - cvVolume = new MatrixMap3[] { null, new MatrixMap3Impl( - List.of(UtilizationClass.values()), List.of(VolumeVariable.values()), List.of(LayerType.values()), - (uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f - ) }; + cvVolume = new MatrixMap3[] { null, + new MatrixMap3Impl( + List.of(UtilizationClass.values()), VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, + List.of(LayerType.values()), + (uc, vv, lt) -> 11f + vv.ordinal() * 2f + uc.ordinal() * 3f + lt.ordinal() * 5f + ) }; cvBa = new MatrixMap2[] { null, new MatrixMap2Impl( @@ -195,8 +197,9 @@ void cleanup() { void testFailBeforeSet() throws ProcessingException { assertThrows( IllegalStateException.class, - () -> unit.getCVVolume(1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY), - "getCVVolume" + () -> unit.getCVVolume( + 1, UtilizationClass.ALL, UtilizationClassVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY + ), "getCVVolume" ); assertThrows( IllegalStateException.class, () -> unit.getCVBasalArea(1, UtilizationClass.ALL, LayerType.PRIMARY), @@ -219,16 +222,35 @@ void testSucceedAfterSet() throws ProcessingException { assertThat( unit, testCV( - "getCVVolume", 1, UtilizationClass.ALL, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, - is(16f) + "getCVVolume", 1, UtilizationClass.ALL, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, + is( + 11f + UtilizationClassVariable.CLOSE_UTIL_VOL.ordinal() * 2f + + UtilizationClass.ALL.ordinal() * 3f + LayerType.PRIMARY.ordinal() * 5f + ) ) ); - assertThat(unit, testCV("getCVBasalArea", 1, UtilizationClass.ALL, LayerType.PRIMARY, is(16f))); assertThat( unit, - testCV("getCVQuadraticMeanDiameter", 1, UtilizationClass.U125TO175, LayerType.PRIMARY, is(26f)) + testCV( + "getCVBasalArea", 1, UtilizationClass.ALL, LayerType.PRIMARY, + is(13f + UtilizationClass.ALL.ordinal() * 3f + LayerType.PRIMARY.ordinal() * 5f) + ) + ); + assertThat( + unit, + testCV( + "getCVQuadraticMeanDiameter", 1, UtilizationClass.U125TO175, LayerType.PRIMARY, + is(17f + UtilizationClass.U125TO175.ordinal() * 3f + LayerType.PRIMARY.ordinal() * 5f) + ) + ); + assertThat( + unit, + testCV( + "getCVSmall", 1, UtilizationClassVariable.QUAD_MEAN_DIAMETER, + is(UtilizationClassVariable.QUAD_MEAN_DIAMETER.ordinal() * 7f) + ) ); - assertThat(unit, testCV("getCVSmall", 1, UtilizationClassVariable.QUAD_MEAN_DIAMETER, is(7f))); } @Test diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index b6ed4ff97..0502281d0 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -17,9 +17,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.io.Writer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -41,7 +38,6 @@ import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.common.ControlKey; -import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.coe.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.coe.BreakageParser; @@ -67,16 +63,14 @@ import ca.bc.gov.nrs.vdyp.model.GenusDefinitionMap; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; -import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class TestUtils { @@ -721,25 +715,22 @@ public static VdypPolygon deepCopy(VdypPolygon poly) { sb.addCompatibilityVariables(cvb -> { - MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( + MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( UtilizationClass.UTIL_CLASSES, - VolumeVariable.ALL, - LayerType.ALL_USED, + VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, (uc, vv, lt) -> cv.getCvVolume(uc, vv, lt) ); MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, - LayerType.ALL_USED, + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> cv.getCvBasalArea(uc, lt) ); MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, - LayerType.ALL_USED, + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> cv.getCvQuadraticMeanDiameter(uc, lt) ); Map cvPrimaryLayerSmall = new HashMap<>(); for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var vv : VolumeVariable.ALL) { + for (var vv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { for (var lt : LayerType.ALL_USED) { cvVolume.put(uc, vv, lt, cv.getCvVolume(uc, vv, lt)); } @@ -757,7 +748,7 @@ public static VdypPolygon deepCopy(VdypPolygon poly) { cvQuadraticMeanDiameter.put(uc, lt, cv.getCvQuadraticMeanDiameter(uc, lt)); } } - for (var ucv : UtilizationClassVariable.ALL) { + for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv)); } @@ -807,31 +798,22 @@ private static String utilVectorLiteral(UtilizationVector uv) { if (uv.size() == 2) { return String.format("Utils.heightVector(%f, %f)", uv.getSmall(), uv.getAll()); } - if (uv.get(UtilizationClass.SMALL) == 0 && - uv.get(UtilizationClass.U75TO125) == 0 && - uv.get(UtilizationClass.U125TO175) == 0 && - uv.get(UtilizationClass.U175TO225) == 0 && - uv.getAll() == uv.getLarge()) { + if (uv.get(UtilizationClass.SMALL) == 0 && uv.get(UtilizationClass.U75TO125) == 0 + && uv.get(UtilizationClass.U125TO175) == 0 && uv.get(UtilizationClass.U175TO225) == 0 + && uv.getAll() == uv.getLarge()) { return String.format("Utils.utilizationVector(%f)", uv.getAll()); } if (Math.abs(UtilizationClass.ALL_CLASSES.stream().mapToDouble(uv::get).sum() - uv.getAll()) < 0.00001) { return String.format( - "Utils.utilizationVector(%f, %f, %f, %f, %f)", - uv.getSmall(), - uv.get(UtilizationClass.U75TO125), - uv.get(UtilizationClass.U125TO175), - uv.get(UtilizationClass.U175TO225), + "Utils.utilizationVector(%f, %f, %f, %f, %f)", uv.getSmall(), uv.get(UtilizationClass.U75TO125), + uv.get(UtilizationClass.U125TO175), uv.get(UtilizationClass.U175TO225), uv.get(UtilizationClass.OVER225) ); } return String.format( - "Utils.utilizationVector(%f, %f, %f, %f, %f, %f) /* ALL does not match sum of bands */", - uv.getSmall(), - uv.getAll(), - uv.get(UtilizationClass.U75TO125), - uv.get(UtilizationClass.U125TO175), - uv.get(UtilizationClass.U175TO225), - uv.get(UtilizationClass.OVER225) + "Utils.utilizationVector(%f, %f, %f, %f, %f, %f) /* ALL does not match sum of bands */", uv.getSmall(), + uv.getAll(), uv.get(UtilizationClass.U75TO125), uv.get(UtilizationClass.U125TO175), + uv.get(UtilizationClass.U175TO225), uv.get(UtilizationClass.OVER225) ); } @@ -856,8 +838,8 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, "var result = VdypPolygon.build(pb -> {"); line( - out, " pb.polygonIdentifier(%s, %d);", stringLiteral(poly.getPolygonIdentifier().getBase()), poly - .getPolygonIdentifier().getYear() + out, " pb.polygonIdentifier(%s, %d);", stringLiteral(poly.getPolygonIdentifier().getBase()), + poly.getPolygonIdentifier().getYear() ); line(out, ""); @@ -875,61 +857,38 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, " lb.layerType(%s);", enumLiteral(layer.getLayerType())); line(out, ""); line( - out, - " lb.empiricalRelationshipParameterIndex(%s);", + out, " lb.empiricalRelationshipParameterIndex(%s);", optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), i -> Integer.toString(i)) ); line(out, ""); line( - out, - " lb.inventoryTypeGroup(%s);", + out, " lb.inventoryTypeGroup(%s);", optionalLiteral(layer.getInventoryTypeGroup(), i -> Integer.toString(i)) ); line(out, ""); + line(out, " lb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); + line(out, " lb.treesPerHectare(%s);", utilVectorLiteral(layer.getTreesPerHectareByUtilization())); line( - out, - " lb.loreyHeight(%s);", - utilVectorLiteral(layer.getLoreyHeightByUtilization()) - ); - line( - out, - " lb.treesPerHectare(%s);", - utilVectorLiteral(layer.getTreesPerHectareByUtilization()) - ); - line( - out, - " lb.quadMeanDiameter(%s);", + out, " lb.quadMeanDiameter(%s);", utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) ); - line( - out, - " lb.baseArea(%s);", - utilVectorLiteral(layer.getBaseAreaByUtilization()) - ); + line(out, " lb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); line(out, ""); + line(out, " lb.wholeStemVolume(%s);", utilVectorLiteral(layer.getWholeStemVolumeByUtilization())); line( - out, - " lb.wholeStemVolume(%s);", - utilVectorLiteral(layer.getWholeStemVolumeByUtilization()) - ); - line( - out, - " lb.closeUtilizationVolumeByUtilization(%s);", + out, " lb.closeUtilizationVolumeByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) ); line( - out, - " lb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", + out, " lb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) ); line( - out, - " lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + out, " lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) ); line( - out, - " lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + out, " lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) ); line(out, ""); @@ -946,40 +905,33 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, ""); line(out, " sb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); line( - out, " sb.treesPerHectare(%s);", utilVectorLiteral( - layer.getTreesPerHectareByUtilization() - ) + out, " sb.treesPerHectare(%s);", + utilVectorLiteral(layer.getTreesPerHectareByUtilization()) ); line( - out, " sb.quadMeanDiameter(%s);", utilVectorLiteral( - layer.getQuadraticMeanDiameterByUtilization() - ) + out, " sb.quadMeanDiameter(%s);", + utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) ); line(out, " sb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); line(out, ""); line( - out, " sb.wholeStemVolume(%s);", utilVectorLiteral( - layer.getWholeStemVolumeByUtilization() - ) + out, " sb.wholeStemVolume(%s);", + utilVectorLiteral(layer.getWholeStemVolumeByUtilization()) ); line( - out, - " sb.closeUtilizationVolumeByUtilization(%s);", + out, " sb.closeUtilizationVolumeByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) ); line( - out, - " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s));", + out, " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s));", utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) ); line( - out, - " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + out, " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) ); line( - out, - " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + out, " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) ); line(out, ""); @@ -997,13 +949,9 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, " (uc, vv, lt) -> 0f"); line(out, " );"); for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var vv : VolumeVariable.ALL) { + for (var vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { for (var lt : LayerType.ALL_USED) { - line( - out, - " cvVolume.put(uc, vv, lt, %f);", - cv.getCvVolume(uc, vv, lt) - ); + line(out, " cvVolume.put(uc, vv, lt, %f);", cv.getCvVolume(uc, vv, lt)); } } } @@ -1019,10 +967,7 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { for (var uc : UtilizationClass.UTIL_CLASSES) { for (var lt : LayerType.ALL_USED) { - line( - out, " cvBasalArea.put(uc, lt, %f);", - cv.getCvBasalArea(uc, lt) - ); + line(out, " cvBasalArea.put(uc, lt, %f);", cv.getCvBasalArea(uc, lt)); } } line(out, " );"); @@ -1039,8 +984,7 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { for (var uc : UtilizationClass.UTIL_CLASSES) { for (var lt : LayerType.ALL_USED) { line( - out, - " cvQuadraticMeanDiameter.put(uc, lt, %f);", + out, " cvQuadraticMeanDiameter.put(uc, lt, %f);", cv.getCvQuadraticMeanDiameter(uc, lt) ); } @@ -1052,7 +996,7 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { " Map cvPrimaryLayerSmall = new HashMap<>();" ); line(out, ""); - for (var ucv : UtilizationClassVariable.ALL) { + for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { line(out, " cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv));"); } line(out, ""); @@ -1094,21 +1038,4 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { } } - /** - * Get the utilization vector for a specified field - * @param holder VdypUtilizationHolder - * @param property The field to get, do not include "ByUtilization" - * @return The UtilizationVector for the named field - * @throws NoSuchMethodException - * @throws SecurityException - * @throws IllegalAccessException - * @throws IllegalArgumentException - * @throws InvocationTargetException - */ - public static UtilizationVector getUtilization(VdypUtilizationHolder holder, String property) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { - var accessor = VdypUtilizationHolder.class.getMethod("get"+property+"ByUtilization"); - var vector = (UtilizationVector) accessor.invoke(holder); - return vector; - } - } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 4775de093..b1318a066 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -24,7 +24,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.BiFunction; import java.util.function.Function; import org.hamcrest.BaseMatcher; @@ -54,8 +53,8 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSite; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; /** * Custom Hamcrest Matchers @@ -624,8 +623,7 @@ public static Matcher isBec(String alias) { return allOf(instanceOf(BecDefinition.class), hasProperty("alias", is(alias))); } - public static Matcher - utilization(Coefficients expected) { + public static Matcher utilization(Coefficients expected) { if (expected.size() == 2) { return utilizationHeight(expected.get(0), expected.get(1)); } @@ -650,17 +648,17 @@ public void describeTo(Description description) { "[Small: %f, All: %f, 7.5cm: %f, 12.5cm: %f, 17.5cm: %f, 22.5cm: %f]", small, all, util1, util2, util3, util4 ); - description.appendText("A utilization vector ").appendValue(utilizationRep); + description.appendText("a utilization vector ").appendValue(utilizationRep); } @Override protected boolean matchesSafely(Coefficients item, Description mismatchDescription) { if (item.size() != 6 || item.getIndexFrom() != -1) { - mismatchDescription.appendText("Was not a utilization vector"); + mismatchDescription.appendText("was not a utilization vector"); return false; } boolean matches = true; - mismatchDescription.appendText("Was [Small: "); + mismatchDescription.appendText("was [Small: "); matches &= matchesComponent(mismatchDescription, small, item.getCoe(UtilizationClass.SMALL.index)); mismatchDescription.appendText(", All: "); matches &= matchesComponent(mismatchDescription, all, item.getCoe(UtilizationClass.ALL.index)); @@ -695,17 +693,17 @@ boolean matchesComponent(Description description, float expected, float result) @Override public void describeTo(Description description) { String utilizationRep = String.format("[Small: %f, All: %f]", small, all); - description.appendText("A lorey height vector ").appendValue(utilizationRep); + description.appendText("a lorey height vector ").appendValue(utilizationRep); } @Override protected boolean matchesSafely(Coefficients item, Description mismatchDescription) { if (item.size() != 2 || item.getIndexFrom() != -1) { - mismatchDescription.appendText("Was not a lorey height vector"); + mismatchDescription.appendText("was not a lorey height vector"); return false; } boolean matches = true; - mismatchDescription.appendText("Was [Small: "); + mismatchDescription.appendText("was [Small: "); matches &= matchesComponent(mismatchDescription, small, item.getCoe(UtilizationClass.SMALL.index)); mismatchDescription.appendText(", All: "); matches &= matchesComponent(mismatchDescription, all, item.getCoe(UtilizationClass.ALL.index)); @@ -766,16 +764,11 @@ static private boolean sep(boolean match, Description mismatchDescription) { return false; } - static private boolean matchValue( - boolean match, String name, T expected, T value, Description mismatchDescription - ) { + static private boolean + matchValue(boolean match, String name, T expected, T value, Description mismatchDescription) { if (!expected.equals(value)) { match = sep(match, mismatchDescription); - mismatchDescription - .appendText(name) - .appendText(" was ") - .appendValue(value) - .appendText(" but expected ") + mismatchDescription.appendText(name).appendText(" was ").appendValue(value).appendText(" but expected ") .appendValue(expected); } return match; @@ -785,17 +778,27 @@ static private boolean matchValue( boolean match, String name, UtilizationVector expected, UtilizationVector value, Description mismatchDescription ) { - return matchValue(match, name, value, utilization(expected), mismatchDescription); + return matchValueShowExpected(match, name, utilization(expected), value, mismatchDescription); } - static private boolean matchValue( + static private boolean + matchValue(boolean match, String name, Matcher expected, T value, Description mismatchDescription) { + if (!expected.matches(value)) { + match = sep(match, mismatchDescription); + mismatchDescription.appendText(name).appendText(" "); + expected.describeMismatch(value, mismatchDescription); + } + return match; + } + + static private boolean matchValueShowExpected( boolean match, String name, Matcher expected, T value, Description mismatchDescription ) { if (!expected.matches(value)) { match = sep(match, mismatchDescription); - mismatchDescription - .appendText(name) - .appendText(" "); + mismatchDescription.appendText(name).appendText(" expected "); + expected.describeTo(mismatchDescription); + mismatchDescription.appendText(" but "); expected.describeMismatch(value, mismatchDescription); } return match; @@ -814,59 +817,38 @@ protected boolean matchesSafely(VdypPolygon item, Description mismatchDescriptio boolean match = true; match = matchValue( - match, "PolygonIdentifier", - expected.getPolygonIdentifier(), - item.getPolygonIdentifier(), + match, "PolygonIdentifier", expected.getPolygonIdentifier(), item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( - match, "BiogeoclimaticZone", - expected.getBiogeoclimaticZone(), - item.getBiogeoclimaticZone(), + match, "BiogeoclimaticZone", expected.getBiogeoclimaticZone(), item.getBiogeoclimaticZone(), mismatchDescription ); match = matchValue( - match, "ForestInventoryZone", - expected.getForestInventoryZone(), - item.getForestInventoryZone(), + match, "ForestInventoryZone", expected.getForestInventoryZone(), item.getForestInventoryZone(), mismatchDescription ); match = matchValue( - match, "InventoryTypeGroup", - expected.getInventoryTypeGroup(), - item.getInventoryTypeGroup(), + match, "InventoryTypeGroup", expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), mismatchDescription ); - match = matchValue( - match, "Mode", - expected.getMode(), - item.getMode(), - mismatchDescription - ); + match = matchValue(match, "Mode", expected.getMode(), item.getMode(), mismatchDescription); match = matchValue( - match, "PercentAvailable", - expected.getPercentAvailable(), - item.getPercentAvailable(), + match, "PercentAvailable", expected.getPercentAvailable(), item.getPercentAvailable(), mismatchDescription ); match = matchValue( - match, "TargetYear", - expected.getTargetYear(), - item.getTargetYear(), - mismatchDescription + match, "TargetYear", expected.getTargetYear(), item.getTargetYear(), mismatchDescription ); match = matchValue( - match, "Layers", - expected.getLayers().keySet(), - item.getLayers().keySet(), - mismatchDescription + match, "Layers", expected.getLayers().keySet(), item.getLayers().keySet(), mismatchDescription ); for (var layerType : expected.getLayers().keySet()) { @@ -896,9 +878,8 @@ public static Matcher deepEquals(VdypLayer expected) { @Override public void describeTo(Description description) { - description.appendText("matches VDYPLayer ").appendValue(expected.getPolygonIdentifier()).appendValue( - expected.getLayerType() - ); + description.appendText("matches VDYPLayer ").appendValue(expected.getPolygonIdentifier()) + .appendValue(expected.getLayerType()); } @Override @@ -906,40 +887,27 @@ protected boolean matchesSafely(VdypLayer item, Description mismatchDescription) boolean match = true; match = matchValue( - match, "PolygonIdentifier", - expected.getPolygonIdentifier(), - item.getPolygonIdentifier(), + match, "PolygonIdentifier", expected.getPolygonIdentifier(), item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( - match, "LayerType", - expected.getLayerType(), - item.getLayerType(), - mismatchDescription + match, "LayerType", expected.getLayerType(), item.getLayerType(), mismatchDescription ); match = matchValue( - match, "InventoryTypeGroup", - expected.getInventoryTypeGroup(), - item.getInventoryTypeGroup(), + match, "InventoryTypeGroup", expected.getInventoryTypeGroup(), item.getInventoryTypeGroup(), mismatchDescription ); match = matchValue( - match, "PrimaryGenus", - expected.getPrimaryGenus(), - item.getPrimaryGenus(), - mismatchDescription + match, "PrimaryGenus", expected.getPrimaryGenus(), item.getPrimaryGenus(), mismatchDescription ); match = matchValue( - match, "EmpiricalRelationshipParameterIndex", - expected.getEmpiricalRelationshipParameterIndex(), - item.getEmpiricalRelationshipParameterIndex(), - mismatchDescription + match, "EmpiricalRelationshipParameterIndex", expected.getEmpiricalRelationshipParameterIndex(), + item.getEmpiricalRelationshipParameterIndex(), mismatchDescription ); match = matchValue( - match, "Species Groups", - expected.getSpecies().keySet(), item.getSpecies().keySet(), + match, "Species Groups", expected.getSpecies().keySet(), item.getSpecies().keySet(), mismatchDescription ); @@ -960,58 +928,45 @@ protected boolean matchesSafely(VdypLayer item, Description mismatchDescription) } match = matchValue( - match, "getLoreyHeightByUtilization", - expected.getLoreyHeightByUtilization(), - item.getLoreyHeightByUtilization(), - mismatchDescription + match, "getLoreyHeightByUtilization", expected.getLoreyHeightByUtilization(), + item.getLoreyHeightByUtilization(), mismatchDescription ); match = matchValue( - match, "getBaseAreaByUtilization", - expected.getBaseAreaByUtilization(), - item.getBaseAreaByUtilization(), - mismatchDescription + match, "getBaseAreaByUtilization", expected.getBaseAreaByUtilization(), + item.getBaseAreaByUtilization(), mismatchDescription ); match = matchValue( match, "getQuadraticMeanDiameterByUtilization", - expected.getQuadraticMeanDiameterByUtilization(), - item.getQuadraticMeanDiameterByUtilization(), + expected.getQuadraticMeanDiameterByUtilization(), item.getQuadraticMeanDiameterByUtilization(), mismatchDescription ); match = matchValue( - match, "getTreesPerHectareByUtilization", - expected.getTreesPerHectareByUtilization(), - item.getTreesPerHectareByUtilization(), - mismatchDescription + match, "getTreesPerHectareByUtilization", expected.getTreesPerHectareByUtilization(), + item.getTreesPerHectareByUtilization(), mismatchDescription ); match = matchValue( - match, "getWholeStemVolumeByUtilization", - expected.getWholeStemVolumeByUtilization(), - item.getWholeStemVolumeByUtilization(), - mismatchDescription + match, "getWholeStemVolumeByUtilization", expected.getWholeStemVolumeByUtilization(), + item.getWholeStemVolumeByUtilization(), mismatchDescription ); match = matchValue( match, "getCloseUtilizationVolumeByUtilization", expected.getCloseUtilizationVolumeByUtilization(), - item.getCloseUtilizationVolumeByUtilization(), - mismatchDescription + item.getCloseUtilizationVolumeByUtilization(), mismatchDescription ); match = matchValue( match, "getCloseUtilizationVolumeNetOfDecayByUtilization", expected.getCloseUtilizationVolumeNetOfDecayByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayByUtilization(), - mismatchDescription + item.getCloseUtilizationVolumeNetOfDecayByUtilization(), mismatchDescription ); match = matchValue( match, "getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization", expected.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - mismatchDescription + item.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), mismatchDescription ); match = matchValue( match, "getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", expected.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - mismatchDescription + item.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), mismatchDescription ); return match; @@ -1026,10 +981,8 @@ public static Matcher deepEquals(VdypSpecies expected) { @Override public void describeTo(Description description) { - description.appendText("matches VDYPSpecies ") - .appendValue(expected.getPolygonIdentifier()) - .appendValue(expected.getLayerType()) - .appendValue(expected.getGenus()); + description.appendText("matches VDYPSpecies ").appendValue(expected.getPolygonIdentifier()) + .appendValue(expected.getLayerType()).appendValue(expected.getGenus()); } @Override @@ -1037,59 +990,33 @@ protected boolean matchesSafely(VdypSpecies item, Description mismatchDescriptio boolean match = true; match = matchValue( - match, "PolygonIdentifier", - expected.getPolygonIdentifier(), - item.getPolygonIdentifier(), + match, "PolygonIdentifier", expected.getPolygonIdentifier(), item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( - match, "LayerType", - expected.getLayerType(), - item.getLayerType(), - mismatchDescription + match, "LayerType", expected.getLayerType(), item.getLayerType(), mismatchDescription ); + match = matchValue(match, "Genus", expected.getGenus(), item.getGenus(), mismatchDescription); match = matchValue( - match, "Genus", - expected.getGenus(), - item.getGenus(), - mismatchDescription - ); - match = matchValue( - match, "GenusIndex", - expected.getGenusIndex(), - item.getGenusIndex(), - mismatchDescription + match, "GenusIndex", expected.getGenusIndex(), item.getGenusIndex(), mismatchDescription ); match = matchValue( - match, "BreakageGroup", - expected.getBreakageGroup(), - item.getBreakageGroup(), + match, "BreakageGroup", expected.getBreakageGroup(), item.getBreakageGroup(), mismatchDescription ); match = matchValue( - match, "VolumeGroup", - expected.getVolumeGroup(), - item.getVolumeGroup(), - mismatchDescription + match, "VolumeGroup", expected.getVolumeGroup(), item.getVolumeGroup(), mismatchDescription ); match = matchValue( - match, "DecayGroup", - expected.getDecayGroup(), - item.getDecayGroup(), - mismatchDescription + match, "DecayGroup", expected.getDecayGroup(), item.getDecayGroup(), mismatchDescription ); match = matchValue( - match, "PercentGenus", - expected.getPercentGenus(), - item.getPercentGenus(), - mismatchDescription + match, "PercentGenus", expected.getPercentGenus(), item.getPercentGenus(), mismatchDescription ); match = matchValue( - match, "FractionGenus", - expected.getFractionGenus(), - item.getFractionGenus(), + match, "FractionGenus", expected.getFractionGenus(), item.getFractionGenus(), mismatchDescription ); @@ -1114,18 +1041,16 @@ protected boolean matchesSafely(VdypSpecies item, Description mismatchDescriptio if (expected.getSite().isEmpty() && item.getSite().isPresent()) { match = sep(match, mismatchDescription); - mismatchDescription.appendText( - "expected not to have Compatibility Variables but they were present" - ); + mismatchDescription + .appendText("expected not to have Compatibility Variables but they were present"); } else if (expected.getSite().isPresent() && item.getSite().isEmpty()) { match = sep(match, mismatchDescription); mismatchDescription.appendText("expected to have Compatibility Variables but none were present"); } match = match && Utils.flatMapBoth( - expected.getCompatibilityVariables(), item.getCompatibilityVariables(), ( - expectedCv, itemCv - ) -> { + expected.getCompatibilityVariables(), item.getCompatibilityVariables(), + (expectedCv, itemCv) -> { var cvMatcher = deepEquals(expectedCv); if (!cvMatcher.matches(itemCv)) { @@ -1141,12 +1066,17 @@ protected boolean matchesSafely(VdypSpecies item, Description mismatchDescriptio ).orElse(true); match = matchValue( - match, "Sp64DistributionSet", - expected.getSp64DistributionSet(), - item.getSp64DistributionSet(), + match, "Sp64DistributionSet", expected.getSp64DistributionSet(), item.getSp64DistributionSet(), mismatchDescription ); + for (var ucv : UtilizationClassVariable.values()) { + var expectedVector = ucv.get(expected); + var actualVector = ucv.get(item); + + match = matchValue(match, ucv.getShortName(), expectedVector, actualVector, mismatchDescription); + } + return match; } @@ -1168,28 +1098,22 @@ protected boolean matchesSafely(VdypCompatibilityVariables item, Description mis boolean match = true; match = matchValue( - match, "CvVolume", - mmEquals(expected.getCvVolume(), VdypMatchers::closeTo), - item.getCvVolume(), + match, "CvVolume", mmEquals(expected.getCvVolume(), VdypMatchers::closeTo), item.getCvVolume(), mismatchDescription ); match = matchValue( - match, "CvBasalArea", - mmEquals(expected.getCvBasalArea(), VdypMatchers::closeTo), - item.getCvBasalArea(), - mismatchDescription + match, "CvBasalArea", mmEquals(expected.getCvBasalArea(), VdypMatchers::closeTo), + item.getCvBasalArea(), mismatchDescription ); match = matchValue( match, "CvQuadraticMeanDiameter", mmEquals(expected.getCvQuadraticMeanDiameter(), VdypMatchers::closeTo), - item.getCvQuadraticMeanDiameter(), - mismatchDescription + item.getCvQuadraticMeanDiameter(), mismatchDescription ); match = matchValue( match, "CvPrimaryLayerSmall", mapEquals(expected.getCvPrimaryLayerSmall(), VdypMatchers::closeTo), - item.getCvPrimaryLayerSmall(), - mismatchDescription + item.getCvPrimaryLayerSmall(), mismatchDescription ); return match; @@ -1197,9 +1121,8 @@ protected boolean matchesSafely(VdypCompatibilityVariables item, Description mis }; } - public static Matcher> mapEquals( - Map expected, Function> valueMatcherGenerator - ) { + public static Matcher> + mapEquals(Map expected, Function> valueMatcherGenerator) { if (expected.isEmpty()) { return Matchers.anEmptyMap(); } @@ -1213,17 +1136,14 @@ public void describeTo(Description description) { } @Override - protected boolean matchesSafely( - Map item, Description mismatchDescription - ) { + protected boolean matchesSafely(Map item, Description mismatchDescription) { if (item.isEmpty()) { mismatchDescription.appendText("map was empty"); return false; } if (!expected.keySet().equals(item.keySet())) { - mismatchDescription.appendText("expected keys ").appendValue(expected.keySet()).appendText( - " but were " - ).appendValue(item.keySet()); + mismatchDescription.appendText("expected keys ").appendValue(expected.keySet()) + .appendText(" but were ").appendValue(item.keySet()); return false; } List failures = new LinkedList<>(); @@ -1248,10 +1168,8 @@ protected boolean matchesSafely( mismatchDescription.appendText(first); if (failures.size() > 1) { - mismatchDescription - .appendText(" and there were ") - .appendText(Integer.toString(failures.size() - 1)) - .appendText(" other mismatches"); + mismatchDescription.appendText(" and there were ") + .appendText(Integer.toString(failures.size() - 1)).appendText(" other mismatches"); } return false; } @@ -1262,9 +1180,8 @@ protected boolean matchesSafely( }; } - public static , T> Matcher mmEquals( - M expected, Function> valueMatcherGenerator - ) { + public static , T> Matcher + mmEquals(M expected, Function> valueMatcherGenerator) { return new TypeSafeDiagnosingMatcher() { @Override @@ -1275,8 +1192,7 @@ public void describeTo(Description description) { @Override protected boolean matchesSafely(M item, Description mismatchDescription) { if (item.getNumDimensions() != expected.getNumDimensions()) { - mismatchDescription - .appendText("matrix map had ") + mismatchDescription.appendText("matrix map had ") .appendText(Integer.toString(item.getNumDimensions())) .appendText(" dimensions but expected ") .appendText(Integer.toString(expected.getNumDimensions())); @@ -1284,10 +1200,8 @@ protected boolean matchesSafely(M item, Description mismatchDescription) { return false; } if (!item.getDimensions().equals(expected.getDimensions())) { - mismatchDescription - .appendText("matrix map had dimensions ") - .appendText(item.getDimensions().toString()) - .appendText(" but expected ") + mismatchDescription.appendText("matrix map had dimensions ") + .appendText(item.getDimensions().toString()).appendText(" but expected ") .appendText(expected.getDimensions().toString()); return false; @@ -1317,10 +1231,8 @@ protected boolean matchesSafely(M item, Description mismatchDescription) { mismatchDescription.appendText(first); if (failures.size() > 1) { - mismatchDescription - .appendText(" and there were ") - .appendText(Integer.toString(failures.size() - 1)) - .appendText(" other mismatches"); + mismatchDescription.appendText(" and there were ") + .appendText(Integer.toString(failures.size() - 1)).appendText(" other mismatches"); } return false; } @@ -1337,68 +1249,39 @@ public static Matcher deepEquals(VdypSite expected) { @Override public void describeTo(Description description) { - description.appendText("matches VDYPSite ") - .appendValue(expected.getPolygonIdentifier()) - .appendValue(expected.getLayerType()) - .appendValue(expected.getSiteGenus()); + description.appendText("matches VDYPSite ").appendValue(expected.getPolygonIdentifier()) + .appendValue(expected.getLayerType()).appendValue(expected.getSiteGenus()); } @Override protected boolean matchesSafely(VdypSite item, Description mismatchDescription) { boolean match = true; match = matchValue( - match, "PolygonIdentifier", - expected.getPolygonIdentifier(), - item.getPolygonIdentifier(), + match, "PolygonIdentifier", expected.getPolygonIdentifier(), item.getPolygonIdentifier(), mismatchDescription ); match = matchValue( - match, "LayerType", - expected.getLayerType(), - item.getLayerType(), - mismatchDescription + match, "LayerType", expected.getLayerType(), item.getLayerType(), mismatchDescription ); match = matchValue( - match, "SpeciesGroup", - expected.getSiteGenus(), - item.getSiteGenus(), - mismatchDescription + match, "SpeciesGroup", expected.getSiteGenus(), item.getSiteGenus(), mismatchDescription ); + match = matchValue(match, "AgeTotal", expected.getAgeTotal(), item.getAgeTotal(), mismatchDescription); + match = matchValue(match, "Height", expected.getHeight(), item.getHeight(), mismatchDescription); match = matchValue( - match, "AgeTotal", - expected.getAgeTotal(), - item.getAgeTotal(), + match, "SiteCurveNumber", expected.getSiteCurveNumber(), item.getSiteCurveNumber(), mismatchDescription ); match = matchValue( - match, "Height", - expected.getHeight(), - item.getHeight(), - mismatchDescription - ); - match = matchValue( - match, "SiteCurveNumber", - expected.getSiteCurveNumber(), - item.getSiteCurveNumber(), - mismatchDescription - ); - match = matchValue( - match, "SiteIndex", - expected.getSiteIndex(), - item.getSiteIndex(), - mismatchDescription + match, "SiteIndex", expected.getSiteIndex(), item.getSiteIndex(), mismatchDescription ); match = matchValue( - match, "YearsToBreastHeight", - expected.getYearsToBreastHeight(), - item.getYearsToBreastHeight(), + match, "YearsToBreastHeight", expected.getYearsToBreastHeight(), item.getYearsToBreastHeight(), mismatchDescription ); match = matchValue( - match, "YearsAtBreastHeight", - expected.getYearsAtBreastHeight(), - item.getYearsAtBreastHeight(), + match, "YearsAtBreastHeight", expected.getYearsAtBreastHeight(), item.getYearsAtBreastHeight(), mismatchDescription ); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index d5b5c6f74..2a4ece6da 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -15,10 +15,12 @@ import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.hamcrest.StringDescription; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; import ca.bc.gov.nrs.vdyp.common.Utils; @@ -28,12 +30,11 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypSite; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; class VdypMatchersTest { @@ -41,10 +42,7 @@ static void assertMismatch(T item, Matcher unit, Matcher descript assertFalse(unit.matches(item), "expected match to fail but it passed"); var description = new StringDescription(); unit.describeMismatch(item, description); - assertThat( - "Mismatch description not as expected", - description.toString(), descriptionMatcher - ); + assertThat("Mismatch description not as expected", description.toString(), descriptionMatcher); } static void assertMatch(T item, Matcher unit) { @@ -59,21 +57,15 @@ static void assertMatch(T item, Matcher unit) { UtilizationVector mockUtilVector(float multiplier) { return Utils.utilizationVector( - rand.nextFloat() * multiplier, - rand.nextFloat() * multiplier, - rand.nextFloat() * multiplier, - rand.nextFloat() * multiplier, - rand.nextFloat() * multiplier + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier ); } UtilizationVector mockHeightVector() { - return Utils.heightVector( - rand.nextFloat() * 5, - rand.nextFloat() * 20 - ); + return Utils.heightVector(rand.nextFloat() * 5, rand.nextFloat() * 20); } - + @Nested class deepEquals { @@ -147,7 +139,8 @@ void testPass() { assertMatch(actual, unit); } - // Changing the key properties also causes mismatches on the children that share those key properties so use startsWith + // Changing the key properties also causes mismatches on the children that share those key properties so use + // startsWith @Test void testPolyIdDifferent() { @@ -162,7 +155,8 @@ void testPolyIdDifferent() { }); assertMismatch( - actual, unit, startsWith( + actual, unit, + startsWith( "PolygonIdentifier was but expected " ) ); @@ -180,11 +174,7 @@ void testLayerDifferent() { sb.layerType(LayerType.VETERAN); }); - assertMismatch( - actual, unit, startsWith( - "LayerType was but expected " - ) - ); + assertMismatch(actual, unit, startsWith("LayerType was but expected ")); } @Test @@ -199,11 +189,7 @@ void testSpeciesGroupDifferent() { sb.genus("S"); }); - assertMismatch( - actual, unit, startsWith( - "Genus was \"S\" but expected \"MB\"" - ) - ); + assertMismatch(actual, unit, startsWith("Genus was \"S\" but expected \"MB\"")); } @Test @@ -218,11 +204,7 @@ void testPercentDifferent() { sb.percentGenus(89); }); - assertMismatch( - actual, unit, equalTo( - "PercentGenus was <89.0F> but expected <90.0F>" - ) - ); + assertMismatch(actual, unit, equalTo("PercentGenus was <89.0F> but expected <90.0F>")); } @Test @@ -237,11 +219,7 @@ void testBreakageGroupDifferent() { sb.breakageGroup(22); }); - assertMismatch( - actual, unit, equalTo( - "BreakageGroup was <22> but expected <12>" - ) - ); + assertMismatch(actual, unit, equalTo("BreakageGroup was <22> but expected <12>")); } @Test @@ -256,11 +234,7 @@ void testDecayGroupDifferent() { sb.decayGroup(23); }); - assertMismatch( - actual, unit, equalTo( - "DecayGroup was <23> but expected <13>" - ) - ); + assertMismatch(actual, unit, equalTo("DecayGroup was <23> but expected <13>")); } @Test @@ -275,11 +249,7 @@ void testVolumeGroupDifferent() { sb.volumeGroup(24); }); - assertMismatch( - actual, unit, equalTo( - "VolumeGroup was <24> but expected <14>" - ) - ); + assertMismatch(actual, unit, equalTo("VolumeGroup was <24> but expected <14>")); } @Test @@ -296,9 +266,8 @@ void testSp64DistributionDifferent() { }); assertMismatch( - actual, unit, equalTo( - "Sp64DistributionSet was <[S[1]:70.0, F[2]:30.0]> but expected <[MB[1]:100.0]>" - ) + actual, unit, + equalTo("Sp64DistributionSet was <[S[1]:70.0, F[2]:30.0]> but expected <[MB[1]:100.0]>") ); } @@ -314,7 +283,8 @@ void testCompatibilityVariablesDifferent() { }); assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "mismatch in Compatibility Variables \"MB\": CvBasalArea at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<\\d\\.\\d+F>\\) and there were \\d+ other mismatches" ) ); @@ -332,15 +302,15 @@ void testSiteDifferent() { }); assertMismatch( - actual, unit, equalTo( - "mismatch in site \"MB\": Height was but expected " - ) + actual, unit, + equalTo("mismatch in site \"MB\": Height was but expected ") ); } - + @ParameterizedTest - @ValueSource(strings={"LoreyHeight", "BaseArea", "QuadraticMeanDiameter", "TreesPerHectare"}) - void testUtilizationDifferent(String property) throws Exception { + @EnumSource(UtilizationClassVariable.class) + void testUtilizationDifferent(UtilizationClassVariable ucv) throws Exception { + var actual = VdypSpecies.build(sb -> { sb.copy(expected); sb.copySiteFrom(expected, (ib, i) -> { @@ -350,19 +320,33 @@ void testUtilizationDifferent(String property) throws Exception { }); // Make a change to one entry (SMALL) in the utilization vector for the specified field - - TestUtils.getUtilization(actual, property).scalarInPlace(UtilizationClass.SMALL, x->x+1); - + + ucv.get(actual).scalarInPlace(UtilizationClass.SMALL, x -> x + 1); + + String name = ucv.getShortName(); + String type = "a utilization vector"; + String floatPattern = "(\\d+\\.\\d+)"; + String expectedVectorPattern = "\\[Small: " + floatPattern + ", All: " + floatPattern + ", 7.5cm: " + + floatPattern + ", 12.5cm: " + floatPattern + ", 17.5cm: " + floatPattern + ", 22.5cm: " + + floatPattern + "\\]"; + + String actualVectorPattern = "\\[Small: \\[\\[" + floatPattern + + "\\]\\], All: \\2, 7.5cm: \\3, 12.5cm: \\4, 17.5cm: \\5, 22.5cm: \\6\\]"; + + if (UtilizationClassVariable.LOREY_HEIGHT == ucv) { + type = "a lorey height vector"; + expectedVectorPattern = "\\[Small: " + floatPattern + ", All: " + floatPattern + "\\]"; + actualVectorPattern = "\\[Small: \\[\\[" + floatPattern + "\\]\\], All: \\2\\]"; + } + assertMismatch( - actual, unit, equalTo( - "" + actual, unit, + matchesRegex( + "^" + name + " expected " + type + " \"" + expectedVectorPattern + "\" but was " + + actualVectorPattern + "$" ) ); - - } - - } @@ -405,11 +389,12 @@ void testOneVolumeEntryDifferent() { }); actual.getCvVolume().put( - UtilizationClass.U125TO175, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, 20f + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, 20f ); assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "CvVolume at \\[, , \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" ) ); @@ -421,12 +406,11 @@ void testOneBaEntryDifferent() { cvb.copy(expected); }); - actual.getCvBasalArea().put( - UtilizationClass.U125TO175, LayerType.PRIMARY, 20f - ); + actual.getCvBasalArea().put(UtilizationClass.U125TO175, LayerType.PRIMARY, 20f); assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "CvBasalArea at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" ) ); @@ -438,12 +422,11 @@ void testOneDqEntryDifferent() { cvb.copy(expected); }); - actual.getCvQuadraticMeanDiameter().put( - UtilizationClass.U125TO175, LayerType.PRIMARY, 20f - ); + actual.getCvQuadraticMeanDiameter().put(UtilizationClass.U125TO175, LayerType.PRIMARY, 20f); assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "CvQuadraticMeanDiameter at \\[, \\] expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" ) ); @@ -455,18 +438,16 @@ void testOneSmallEntryDifferent() { cvb.copy(expected); }); - actual.getCvPrimaryLayerSmall().put( - UtilizationClassVariable.BASAL_AREA, 20f - ); + actual.getCvPrimaryLayerSmall().put(UtilizationClassVariable.BASAL_AREA, 20f); assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "CvPrimaryLayerSmall at expected <\\d\\.\\d+F> but it was a java.lang.Float \\(<20.0F>\\)" ) ); } - } @Nested @@ -507,7 +488,8 @@ void testPolyIdDifferent() { }); assertMismatch( - actual, unit, equalTo( + actual, unit, + equalTo( "PolygonIdentifier was but expected " ) ); @@ -520,11 +502,7 @@ void testLayerTypeDifferent() { ib.layerType(LayerType.VETERAN); }); - assertMismatch( - actual, unit, equalTo( - "LayerType was but expected " - ) - ); + assertMismatch(actual, unit, equalTo("LayerType was but expected ")); } @Test @@ -534,11 +512,7 @@ void testSpeciesGroupDifferent() { ib.genus("B"); }); - assertMismatch( - actual, unit, equalTo( - "SpeciesGroup was \"B\" but expected \"MB\"" - ) - ); + assertMismatch(actual, unit, equalTo("SpeciesGroup was \"B\" but expected \"MB\"")); } @Test @@ -548,11 +522,7 @@ void testHeightDifferent() { ib.height(16); }); - assertMismatch( - actual, unit, equalTo( - "Height was but expected " - ) - ); + assertMismatch(actual, unit, equalTo("Height was but expected ")); } @Test @@ -564,7 +534,8 @@ void testAgeTotalDifferent() { // Years at breast height is computed from AgeTotal and YearstToBreastHeight assertMismatch( - actual, unit, equalTo( + actual, unit, + equalTo( "AgeTotal was but expected , YearsAtBreastHeight was but expected " ) ); @@ -579,7 +550,8 @@ void testYearsToBreastHeightDifferent() { // Years at breast height is computed from AgeTotal and YearstToBreastHeight assertMismatch( - actual, unit, equalTo( + actual, unit, + equalTo( "YearsToBreastHeight was but expected , YearsAtBreastHeight was but expected " ) ); @@ -592,11 +564,7 @@ void testSiteCurveNumberDifferent() { ib.siteCurveNumber(41); }); - assertMismatch( - actual, unit, equalTo( - "SiteCurveNumber was but expected " - ) - ); + assertMismatch(actual, unit, equalTo("SiteCurveNumber was but expected ")); } @Test @@ -606,11 +574,7 @@ void testSiteIndexDifferent() { ib.siteIndex(3); }); - assertMismatch( - actual, unit, equalTo( - "SiteIndex was but expected " - ) - ); + assertMismatch(actual, unit, equalTo("SiteIndex was but expected ")); } } @@ -650,9 +614,8 @@ void testTwoCellsDifferent() { test.put(2, 5, "DIFFERENT"); var unit = VdypMatchers.mmEquals(expected, s -> Matchers.equalTo(s)); assertMismatch( - test, unit, equalTo( - "at [<1>, <4>] expected \"1:4\" but it was \"DIFFERENT\" and there were 1 other mismatches" - ) + test, unit, + equalTo("at [<1>, <4>] expected \"1:4\" but it was \"DIFFERENT\" and there were 1 other mismatches") ); } @@ -662,11 +625,7 @@ void testDifferentDimensionality() { List.of(1), List.of(2), List.of(3), (k1, k2, k3) -> "TEST" ); var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); - assertMismatch( - test3, unit, equalTo( - "matrix map had 3 dimensions but expected 2" - ) - ); + assertMismatch(test3, unit, equalTo("matrix map had 3 dimensions but expected 2")); } @Test @@ -675,9 +634,8 @@ void testDimensionMissingValue() { var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); assertMismatch( - test, unit, equalTo( - "matrix map had dimensions [[1, 2], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]" - ) + test, unit, + equalTo("matrix map had dimensions [[1, 2], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]") ); } @@ -689,9 +647,8 @@ void testDimensionExtraValue() { var unit = VdypMatchers.mmEquals((MatrixMap) expected, s -> Matchers.equalTo(s)); assertMismatch( - test, unit, equalTo( - "matrix map had dimensions [[0, 1, 2, 3], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]" - ) + test, unit, + equalTo("matrix map had dimensions [[0, 1, 2, 3], [4, 5, 6]] but expected [[1, 2, 3], [4, 5, 6]]") ); } @@ -732,7 +689,8 @@ void testTwoEntriesDifferent() { test.put(2, "DIFFERENT"); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); assertMismatch( - test, unit, matchesRegex( + test, unit, + matchesRegex( "at <\\d> expected \"Value \\d\" but it was \"DIFFERENT\" and there were 1 other mismatches" ) ); @@ -743,11 +701,7 @@ void testMissingEntry() { test.remove(1); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); - assertMismatch( - test, unit, equalTo( - "expected keys <[1, 2]> but were <[2]>" - ) - ); + assertMismatch(test, unit, equalTo("expected keys <[1, 2]> but were <[2]>")); } @Test @@ -755,11 +709,7 @@ void testExtraEntry() { test.put(42, "EXTRA"); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); - assertMismatch( - test, unit, equalTo( - "expected keys <[1, 2]> but were <[1, 2, 42]>" - ) - ); + assertMismatch(test, unit, equalTo("expected keys <[1, 2]> but were <[1, 2, 42]>")); } @Test @@ -767,27 +717,21 @@ void testBothEmpty() { expected = new HashMap<>(); test = new HashMap<>(); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); - assertMatch( - test, unit - ); + assertMatch(test, unit); } @Test void testExpectedEmpty() { expected = new HashMap<>(); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); - assertMismatch( - test, unit, equalTo("map size was <2>") - ); + assertMismatch(test, unit, equalTo("map size was <2>")); } @Test void testActualEmpty() { test = new HashMap<>(); var unit = VdypMatchers.mapEquals(expected, s -> Matchers.equalTo(s)); - assertMismatch( - test, unit, equalTo("map was empty") - ); + assertMismatch(test, unit, equalTo("map was empty")); } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index e70fefa28..5ed660c63 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -60,13 +60,13 @@ import ca.bc.gov.nrs.vdyp.model.SiteCurveAgeMaximum; import ca.bc.gov.nrs.vdyp.model.Sp64Distribution; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.si32.site.SiteTool; @@ -1548,7 +1548,7 @@ private void calculateSmallComponentYields(LayerProcessingState lps) throws Proc if (controlVar3Value >= 2 && meanVolumeSmall > 0.0f) { meanVolumeSmall *= FloatMath - .exp(lps.getCVSmall(speciesIndex, UtilizationClassVariable.WHOLE_STEM_VOLUME)); + .exp(lps.getCVSmall(speciesIndex, UtilizationClassVariable.WHOLE_STEM_VOL)); } } @@ -2443,8 +2443,9 @@ void setCompatibilityVariables() throws ProcessingException { UtilizationVector quadMeanDiameters = Utils.utilizationVector(); UtilizationVector treesPerHectare = Utils.utilizationVector(); - cvVolume[s] = new MatrixMap3Impl( - UtilizationClass.UTIL_CLASSES, VolumeVariable.ALL, LayerType.ALL_USED, (k1, k2, k3) -> 0f + cvVolume[s] = new MatrixMap3Impl( + UtilizationClass.UTIL_CLASSES, VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, + LayerType.ALL_USED, (k1, k2, k3) -> 0f ); cvBasalArea[s] = new MatrixMap2Impl( UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (k1, k2) -> 0f @@ -2491,8 +2492,10 @@ void setCompatibilityVariables() throws ProcessingException { adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); } - cvVolume[s] - .put(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, adjustment); + cvVolume[s].put( + uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + adjustment + ); // Volume less decay adjustment = 0.0f; @@ -2513,7 +2516,7 @@ void setCompatibilityVariables() throws ProcessingException { adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); } - cvVolume[s].put(uc, VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY, LayerType.PRIMARY, adjustment); + cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, LayerType.PRIMARY, adjustment); // Volume adjustment = 0.0f; @@ -2533,7 +2536,7 @@ void setCompatibilityVariables() throws ProcessingException { adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); } - cvVolume[s].put(uc, VolumeVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, adjustment); + cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, adjustment); } int primarySpeciesVolumeGroup = lps.getVolumeEquationGroups()[s]; @@ -2557,7 +2560,7 @@ void setCompatibilityVariables() throws ProcessingException { ); } - cvVolume[s].put(uc, VolumeVariable.WHOLE_STEM_VOL, LayerType.PRIMARY, adjustment); + cvVolume[s].put(uc, UtilizationClassVariable.WHOLE_STEM_VOL, LayerType.PRIMARY, adjustment); } fps.estimators.estimateQuadMeanDiameterByUtilization(lps.getBecZone(), quadMeanDiameters, genusName); @@ -2677,10 +2680,10 @@ void setCompatibilityVariables() throws ProcessingException { var spWsVolumeSmall = FloatMath .log(spInputWholeStemVolume_Small / spInputTreePerHectare_Small / spMeanVolumeSmall); - spCvSmall.put(UtilizationClassVariable.WHOLE_STEM_VOLUME, spWsVolumeSmall); + spCvSmall.put(UtilizationClassVariable.WHOLE_STEM_VOL, spWsVolumeSmall); } else { - spCvSmall.put(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0f); + spCvSmall.put(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0f); } return spCvSmall; diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java index 9658b589a..bf5084dfd 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java @@ -13,11 +13,11 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; -import ca.bc.gov.nrs.vdyp.model.VolumeVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; class LayerProcessingState { @@ -100,7 +100,7 @@ class LayerProcessingState { // Compatibility Variables - LCV1 & LCVS private boolean areCompatibilityVariablesSet = false; - private MatrixMap3[] cvVolume; + private MatrixMap3[] cvVolume; private MatrixMap2[] cvBasalArea; private MatrixMap2[] cvQuadraticMeanDiameter; private Map[] cvPrimaryLayerSmall; @@ -231,7 +231,7 @@ public int[] getSiteCurveNumbers() { return siteCurveNumbers; } - public MatrixMap3[] getCvVolume() { + public MatrixMap3[] getCvVolume() { return cvVolume; } @@ -377,7 +377,7 @@ public void updatePrimarySpeciesDetailsAfterGrowth(float newPrimarySpeciesDomina } public void setCompatibilityVariableDetails( - MatrixMap3[] cvVolume, + MatrixMap3[] cvVolume, MatrixMap2[] cvBasalArea, MatrixMap2[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall @@ -420,7 +420,7 @@ public void updateCompatibilityVariablesAfterGrowth() { * compVarAdjustments.getValue(uc, UtilizationClassVariable.QUAD_MEAN_DIAMETER) ); - for (VolumeVariable vv : VolumeVariable.ALL) { + for (UtilizationClassVariable vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { cvVolume[i].put( uc, vv, LayerType.PRIMARY, cvVolume[i].get(uc, vv, LayerType.PRIMARY) * compVarAdjustments.getVolumeValue(uc, vv) @@ -430,8 +430,9 @@ public void updateCompatibilityVariablesAfterGrowth() { } } - public float - getCVVolume(int speciesIndex, UtilizationClass uc, VolumeVariable volumeVariable, LayerType layerType) { + public float getCVVolume( + int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable, LayerType layerType + ) { if (!areCompatibilityVariablesSet) { throw new IllegalStateException(UNSET_CV_VOLUMES); } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParserTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParserTest.java index 085eb91ce..aed8dc5d4 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParserTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ForwardControlParserTest.java @@ -44,7 +44,7 @@ import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.SiteCurveAgeMaximum; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.test.TestUtils; @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index 4c62cc53e..bd6c22a8b 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -19,8 +19,8 @@ import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParseException; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; -import ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; class Grow11UpdateCompatibilityVariables { @@ -66,7 +66,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, -2.1394816e-07f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), Matchers.hasEntry(UtilizationClassVariable.LOREY_HEIGHT, 0.0f), - Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0f) + Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0f) ) ); assertThat( @@ -77,7 +77,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, -4.406223e-5f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0023196794f), Matchers.hasEntry(UtilizationClassVariable.LOREY_HEIGHT, 1.2850753e-6f), - Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0010083826f) + Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0010083826f) ) ); assertThat( @@ -88,7 +88,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 4.8476713e-6f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), Matchers.hasEntry(UtilizationClassVariable.LOREY_HEIGHT, -1.5245796e-5f), - Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0f) + Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0f) ) ); assertThat( @@ -98,7 +98,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 0.0f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), Matchers.hasEntry(UtilizationClassVariable.LOREY_HEIGHT, 0.0f), - Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0f) + Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0f) ) ); assertThat( @@ -108,7 +108,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 3.352447e-6f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), Matchers.hasEntry(UtilizationClassVariable.LOREY_HEIGHT, -5.6603396e-5f), - Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOLUME, 0.0f) + Matchers.hasEntry(UtilizationClassVariable.WHOLE_STEM_VOL, 0.0f) ) ); } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java index d43b2d080..fa3f6d219 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java @@ -4,14 +4,13 @@ import static ca.bc.gov.nrs.vdyp.model.UtilizationClass.U125TO175; import static ca.bc.gov.nrs.vdyp.model.UtilizationClass.U175TO225; import static ca.bc.gov.nrs.vdyp.model.UtilizationClass.U75TO125; -import static ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable.BASAL_AREA; -import static ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable.LOREY_HEIGHT; -import static ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable.QUAD_MEAN_DIAMETER; -import static ca.bc.gov.nrs.vdyp.model.UtilizationClassVariable.WHOLE_STEM_VOLUME; -import static ca.bc.gov.nrs.vdyp.model.VolumeVariable.CLOSE_UTIL_VOL; -import static ca.bc.gov.nrs.vdyp.model.VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY; -import static ca.bc.gov.nrs.vdyp.model.VolumeVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE; -import static ca.bc.gov.nrs.vdyp.model.VolumeVariable.WHOLE_STEM_VOL; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.BASAL_AREA; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.LOREY_HEIGHT; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.QUAD_MEAN_DIAMETER; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.WHOLE_STEM_VOL; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.CLOSE_UTIL_VOL; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY; +import static ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -248,26 +247,26 @@ void testSetCompatibilityVariables() throws ResourceParseException, IOException, assertThat(lps.getCVSmall(1, BASAL_AREA), is(-2.1831444E-7f)); assertThat(lps.getCVSmall(1, QUAD_MEAN_DIAMETER), is(0.0f)); assertThat(lps.getCVSmall(1, LOREY_HEIGHT), is(0.0f)); - assertThat(lps.getCVSmall(1, WHOLE_STEM_VOLUME), is(0.0f)); + assertThat(lps.getCVSmall(1, WHOLE_STEM_VOL), is(0.0f)); assertThat(lps.getCVSmall(2, BASAL_AREA), is(-4.496146E-5f)); assertThat(lps.getCVSmall(2, QUAD_MEAN_DIAMETER), is(0.0023670197f)); assertThat(lps.getCVSmall(2, LOREY_HEIGHT), is(1.3113013E-6f)); - assertThat(lps.getCVSmall(2, WHOLE_STEM_VOLUME), is(0.0010289619f)); + assertThat(lps.getCVSmall(2, WHOLE_STEM_VOL), is(0.0010289619f)); assertThat(lps.getCVSmall(3, BASAL_AREA), is(4.9466034E-6f)); assertThat(lps.getCVSmall(3, QUAD_MEAN_DIAMETER), is(0.0f)); assertThat(lps.getCVSmall(3, LOREY_HEIGHT), is(-1.5556934E-5f)); - assertThat(lps.getCVSmall(3, WHOLE_STEM_VOLUME), is(0.0f)); + assertThat(lps.getCVSmall(3, WHOLE_STEM_VOL), is(0.0f)); assertThat(lps.getCVSmall(4, BASAL_AREA), is(0.0f)); assertThat(lps.getCVSmall(4, QUAD_MEAN_DIAMETER), is(0.0f)); assertThat(lps.getCVSmall(4, LOREY_HEIGHT), is(0.0f)); - assertThat(lps.getCVSmall(4, WHOLE_STEM_VOLUME), is(0.0f)); + assertThat(lps.getCVSmall(4, WHOLE_STEM_VOL), is(0.0f)); assertThat(lps.getCVSmall(5, BASAL_AREA), is(3.4208642E-6f)); assertThat(lps.getCVSmall(5, QUAD_MEAN_DIAMETER), is(0.0f)); assertThat(lps.getCVSmall(5, LOREY_HEIGHT), is(-5.7758567E-5f)); - assertThat(lps.getCVSmall(5, WHOLE_STEM_VOLUME), is(0.0f)); + assertThat(lps.getCVSmall(5, WHOLE_STEM_VOL), is(0.0f)); } } From 9f24b55faa002c70b286d11202f718c05b5850a7 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 13:13:54 -0800 Subject: [PATCH 31/45] Fix predecessor method --- .../src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java index f3df8151f..e1cccc23a 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java @@ -589,7 +589,7 @@ public static > T successorOrThrow(T current, T[] values) { * @throws IllegalStateException If the given Enum value is the first one */ public static > T predecessorOrThrow(T current, T[] values) { - return successor(current, values) + return predecessor(current, values) .orElseThrow(() -> new IllegalStateException(MessageFormat.format("{} has no predecessor", current))); } From 62cfa122866ee58830500f575cadd7689d51f398 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 13:21:45 -0800 Subject: [PATCH 32/45] Fix incorrect UCVs being used after refactor --- .../main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java | 2 +- .../java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java index ba1a08b1f..3d2622ef7 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/BackProcessingEngine.java @@ -91,7 +91,7 @@ void prepare(BackProcessingState state) throws ProcessingException { .put(uc, primaryState.getCVQuadraticMeanDiameter(specIndex, uc, LayerType.PRIMARY)); } - for (var ucv : UtilizationClassVariable.values()) { + for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { cvPrimaryLayerSmall[specIndex].put(ucv, primaryState.getCVSmall(specIndex, ucv)); } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java index bf5084dfd..b9f8a0bd8 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java @@ -402,7 +402,7 @@ public void updateCompatibilityVariablesAfterGrowth() { var compVarAdjustments = fps.fcm.getCompVarAdjustments(); for (int i : getIndices()) { - for (UtilizationClassVariable sucv : UtilizationClassVariable.values()) { + for (UtilizationClassVariable sucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { cvPrimaryLayerSmall[i].put( sucv, cvPrimaryLayerSmall[i].get(sucv) * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) From c105a9726bf987c33e246957c8f5adb8797d3f21 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 15:39:42 -0800 Subject: [PATCH 33/45] Refactor Forward to use common processing state base classes --- .../BackLayerProcessingState.java | 6 - .../processing_state/BackProcessingState.java | 3 +- .../LayerProcessingState.java | 21 +- .../processing_state/ProcessingState.java | 7 +- .../LayerProcessingStateTest.java | 6 - ....java => ForwardLayerProcessingState.java} | 205 +++++------------- .../vdyp/forward/ForwardProcessingEngine.java | 199 +++++++++-------- .../vdyp/forward/ForwardProcessingState.java | 81 +------ .../nrs/vdyp/forward/ForwardProcessor.java | 2 +- .../Grow11UpdateCompatibilityVariables.java | 2 +- .../Grow2CalculateBasalAreaDeltaTest.java | 2 +- ...ow3CalculateQuadMeanDiameterDeltaTest.java | 12 +- ...row4CalculateLoreyHeightEstimatesTest.java | 7 +- .../vdyp/forward/Grow5SpeciesBaDqTphTest.java | 10 +- .../forward/Grow6TreesPerHectareTest.java | 2 +- .../forward/Grow7QuadMeanDiameterTest.java | 2 +- .../Grow8PerSpeciesLoreyHeightTest.java | 8 +- .../Grow9PercentagesOfForestedLand.java | 2 +- ...inaryForwardProcessingEngineStepsTest.java | 10 +- ...liminarySetCompatibilityVariablesTest.java | 2 +- .../ProcessMultiplePolygonsBasicTest.java | 6 +- .../vdyp/forward/ProcessPolygonBasicTest.java | 6 +- .../vdyp/forward/test/ForwardTestUtils.java | 3 +- 23 files changed, 229 insertions(+), 375 deletions(-) rename lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/{LayerProcessingState.java => ForwardLayerProcessingState.java} (63%) diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java index 823759fbe..58eeeab07 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackLayerProcessingState.java @@ -28,12 +28,6 @@ protected Predicate getBankFilter() { return x -> true; } - @Override - protected void applyCompatibilityVariables(VdypSpecies species, int i) { - // TODO Auto-generated method stub - - } - @Override protected VdypLayer updateLayerFromBank() { // TODO Auto-generated method stub diff --git a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java index a5976a254..d00870b1c 100644 --- a/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java +++ b/lib/vdyp-back/src/main/java/ca/bc/gov/nrs/vdyp/back/processing_state/BackProcessingState.java @@ -69,8 +69,7 @@ public Optional getBaseAreaVeteran() { public void setCompatibilityVariableDetails( MatrixMap2[] cvVolume, - Map[] cvBasalArea, - Map[] cvQuadraticMeanDiameter, + Map[] cvBasalArea, Map[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall ) { if (areCompatibilityVariablesSet) { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index 36027cebf..b58d661b3 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -51,8 +51,7 @@ public abstract class LayerProcessingState[] cvQuadraticMeanDiameter; private Map[] cvPrimaryLayerSmall; - protected LayerProcessingState(ProcessingState ps, VdypPolygon polygon, LayerType subjectLayerType) - throws ProcessingException { + protected LayerProcessingState(ProcessingState ps, VdypPolygon polygon, LayerType subjectLayerType) { this.ps = ps; this.polygon = polygon; @@ -90,8 +89,6 @@ public Bank getBank() { return bank; } - protected abstract void applyCompatibilityVariables(VdypSpecies species, int i); - public int getNSpecies() { return bank.getNSpecies(); } @@ -150,4 +147,20 @@ public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { return cvPrimaryLayerSmall[speciesIndex].get(variable); } + public MatrixMap3[] getCvVolume() { + return cvVolume; + } + + public MatrixMap2[] getCvBasalArea() { + return cvBasalArea; + } + + public MatrixMap2[] getCvQuadraticMeanDiameter() { + return cvQuadraticMeanDiameter; + } + + public Map[] getCvPrimaryLayerSmall() { + return cvPrimaryLayerSmall; + } + } \ No newline at end of file diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java index 34bb91d59..4146adcd0 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/ProcessingState.java @@ -31,7 +31,7 @@ public ComputationMethods getComputers() { } /** The computation instance used by this engine */ - final ComputationMethods computers; + private final ComputationMethods computers; /** The polygon on which the Processor is currently operating */ private VdypPolygon polygon; @@ -113,4 +113,9 @@ public VdypPolygon updatePolygon() { return polygon; } + + public RCM getControlMap() { + return controlMap; + } + } \ No newline at end of file diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java index 14e720395..55b6eac26 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingStateTest.java @@ -50,12 +50,6 @@ protected Predicate getBankFilter() { return spec -> true; } - @Override - protected void applyCompatibilityVariables(VdypSpecies species, int i) { - // TODO Auto-generated method stub - - } - @Override protected VdypLayer updateLayerFromBank() { // TODO Auto-generated method stub diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java similarity index 63% rename from lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java rename to lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java index b9f8a0bd8..1086a60c7 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/LayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java @@ -3,10 +3,12 @@ import java.util.Arrays; import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; import ca.bc.gov.nrs.vdyp.forward.model.ControlVariable; import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; @@ -16,13 +18,14 @@ import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; -class LayerProcessingState { +class ForwardLayerProcessingState extends LayerProcessingState { - private static final String COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY = "CompatibilityVariablesSet can be set once only"; private static final String PRIMARY_SPECIES_DETAILS_CAN_BE_SET_ONCE_ONLY = "PrimarySpeciesDetails can be set once only"; private static final String SITE_CURVE_NUMBERS_CAN_BE_SET_ONCE_ONLY = "SiteCurveNumbers can be set once only"; private static final String SPECIES_RANKING_DETAILS_CAN_BE_SET_ONCE_ONLY = "SpeciesRankingDetails can be set once only"; @@ -30,27 +33,11 @@ class LayerProcessingState { private static final String UNSET_PRIMARY_SPECIES_AGE_TO_BREAST_HEIGHT = "unset primarySpeciesAgeToBreastHeight"; private static final String UNSET_PRIMARY_SPECIES_AGE_AT_BREAST_HEIGHT = "unset primarySpeciesAgeAtBreastHeight"; private static final String UNSET_PRIMARY_SPECIES_DOMINANT_HEIGHT = "unset primarySpeciesDominantHeight"; - private static final String UNSET_CV_VOLUMES = "unset cvVolumes"; - private static final String UNSET_CV_BASAL_AREAS = "unset cvBasalAreas"; private static final String UNSET_RANKING_DETAILS = "unset rankingDetails"; private static final String UNSET_SITE_CURVE_NUMBERS = "unset siteCurveNumbers"; private static final String UNSET_INVENTORY_TYPE_GROUP = "unset inventoryTypeGroup"; - private static final Logger logger = LoggerFactory.getLogger(LayerProcessingState.class); - - /** The containing ForwardProcessingState */ - private final ForwardProcessingState fps; - - /** The type of Layer being processed */ - private final LayerType layerType; - - // L1COM1, L1COM4 and L1COM5 - these common blocks mirror BANK1, BANK2 and BANK3 and are initialized - // when copied to "active" in ForwardProcessingEngine. - - /** - * State of the layer during processing. - */ - private Bank bank; + private static final Logger logger = LoggerFactory.getLogger(ForwardLayerProcessingState.class); // L1COM2 - equation groups. From the configuration, narrowed to the // polygon's BEC zone. @@ -97,45 +84,31 @@ class LayerProcessingState { private float primarySpeciesAgeAtBreastHeight; // AGEBHP private float primarySpeciesAgeToBreastHeight; // YTBHP - // Compatibility Variables - LCV1 & LCVS - private boolean areCompatibilityVariablesSet = false; - - private MatrixMap3[] cvVolume; - private MatrixMap2[] cvBasalArea; - private MatrixMap2[] cvQuadraticMeanDiameter; - private Map[] cvPrimaryLayerSmall; - // FRBASP0 - FR // TODO // MNSP - MSPL1, MSPLV // TODO - public LayerProcessingState(ForwardProcessingState fps, VdypLayer layer) { - - this.fps = fps; - this.layerType = layer.getLayerType(); + public ForwardLayerProcessingState(ForwardProcessingState fps, VdypPolygon poly, VdypLayer layer) { - bank = new Bank( - layer, fps.getCurrentBecZone(), - s -> s.getBaseAreaByUtilization().get(UtilizationClass.ALL) >= ForwardProcessingEngine.MIN_BASAL_AREA - ); + super(fps, poly, layer.getLayerType()); - var volumeEquationGroupMatrix = this.fps.fcm.getVolumeEquationGroups(); - var decayEquationGroupMatrix = this.fps.fcm.getDecayEquationGroups(); - var breakageEquationGroupMatrix = this.fps.fcm.getBreakageEquationGroups(); + var volumeEquationGroupMatrix = this.getParent().getControlMap().getVolumeEquationGroups(); + var decayEquationGroupMatrix = this.getParent().getControlMap().getDecayEquationGroups(); + var breakageEquationGroupMatrix = this.getParent().getControlMap().getBreakageEquationGroups(); - volumeEquationGroups = new int[bank.getNSpecies() + 1]; - decayEquationGroups = new int[bank.getNSpecies() + 1]; - breakageEquationGroups = new int[bank.getNSpecies() + 1]; + volumeEquationGroups = new int[getBank().getNSpecies() + 1]; + decayEquationGroups = new int[getBank().getNSpecies() + 1]; + breakageEquationGroups = new int[getBank().getNSpecies() + 1]; volumeEquationGroups[0] = VdypEntity.MISSING_INTEGER_VALUE; decayEquationGroups[0] = VdypEntity.MISSING_INTEGER_VALUE; breakageEquationGroups[0] = VdypEntity.MISSING_INTEGER_VALUE; String becZoneAlias = getBecZone().getAlias(); - for (int i : bank.getIndices()) { - String speciesName = bank.speciesNames[i]; + for (int i : getBank().getIndices()) { + String speciesName = getBank().speciesNames[i]; volumeEquationGroups[i] = volumeEquationGroupMatrix.get(speciesName, becZoneAlias); // From VGRPFIND, volumeEquationGroup 10 is mapped to 11. if (volumeEquationGroups[i] == 10) { @@ -146,20 +119,13 @@ public LayerProcessingState(ForwardProcessingState fps, VdypLayer layer) { } } - public LayerType getLayerType() { - return layerType; - } - - public int getNSpecies() { - return bank.getNSpecies(); + @Override + protected Predicate getBankFilter() { + return s -> s.getBaseAreaByUtilization().get(UtilizationClass.ALL) >= ForwardProcessingEngine.MIN_BASAL_AREA; } public int[] getIndices() { - return bank.getIndices(); - } - - public BecDefinition getBecZone() { - return bank.getBecZone(); + return getBank().getIndices(); } public int getPrimarySpeciesIndex() { @@ -173,7 +139,7 @@ public String getPrimarySpeciesAlias() { if (!areRankingDetailsSet) { throw new IllegalStateException("unset primarySpeciesIndex"); } - return bank.speciesNames[primarySpeciesIndex]; + return getBank().speciesNames[primarySpeciesIndex]; } public boolean hasSecondarySpeciesIndex() { @@ -195,14 +161,6 @@ public static Logger getLogger() { return logger; } - public ForwardProcessingState getFps() { - return fps; - } - - public Bank getBank() { - return bank; - } - public int[] getVolumeEquationGroups() { return volumeEquationGroups; } @@ -231,22 +189,6 @@ public int[] getSiteCurveNumbers() { return siteCurveNumbers; } - public MatrixMap3[] getCvVolume() { - return cvVolume; - } - - public MatrixMap2[] getCvBasalArea() { - return cvBasalArea; - } - - public MatrixMap2[] getCvQuadraticMeanDiameter() { - return cvQuadraticMeanDiameter; - } - - public Map[] getCvPrimaryLayerSmall() { - return cvPrimaryLayerSmall; - } - /** * @param n index of species for whom the site curve number is to be returned. * @return the site curve number of the given species. @@ -328,7 +270,7 @@ public void setPrimarySpeciesDetails(PrimarySpeciesDetails details) { // Normally, these values may only be set only once. However, during grow(), if the // control variable UPDATE_DURING_GROWTH_6 has value "1" then updates are allowed. - if (arePrimarySpeciesDetailsSet && fps.fcm.getForwardControlVariables() + if (arePrimarySpeciesDetailsSet && getParent().getControlMap().getForwardControlVariables() .getControlVariable(ControlVariable.UPDATE_DURING_GROWTH_6) != 1) { throw new IllegalStateException(PRIMARY_SPECIES_DETAILS_CAN_BE_SET_ONCE_ONLY); } @@ -340,20 +282,20 @@ public void setPrimarySpeciesDetails(PrimarySpeciesDetails details) { primarySpeciesAgeToBreastHeight = details.primarySpeciesAgeToBreastHeight(); // Store these values into bank if not already set - VHDOM1 lines 182 - 186 - if (bank.dominantHeights[primarySpeciesIndex] <= 0.0) { - bank.dominantHeights[primarySpeciesIndex] = primarySpeciesDominantHeight; + if (getBank().dominantHeights[primarySpeciesIndex] <= 0.0) { + getBank().dominantHeights[primarySpeciesIndex] = primarySpeciesDominantHeight; } - if (bank.siteIndices[primarySpeciesIndex] <= 0.0) { - bank.siteIndices[primarySpeciesIndex] = primarySpeciesSiteIndex; + if (getBank().siteIndices[primarySpeciesIndex] <= 0.0) { + getBank().siteIndices[primarySpeciesIndex] = primarySpeciesSiteIndex; } - if (bank.ageTotals[primarySpeciesIndex] <= 0.0) { - bank.ageTotals[primarySpeciesIndex] = primarySpeciesTotalAge; + if (getBank().ageTotals[primarySpeciesIndex] <= 0.0) { + getBank().ageTotals[primarySpeciesIndex] = primarySpeciesTotalAge; } - if (bank.yearsAtBreastHeight[primarySpeciesIndex] <= 0.0) { - bank.yearsAtBreastHeight[primarySpeciesIndex] = primarySpeciesAgeAtBreastHeight; + if (getBank().yearsAtBreastHeight[primarySpeciesIndex] <= 0.0) { + getBank().yearsAtBreastHeight[primarySpeciesIndex] = primarySpeciesAgeAtBreastHeight; } - if (bank.yearsToBreastHeight[primarySpeciesIndex] <= 0.0) { - bank.yearsToBreastHeight[primarySpeciesIndex] = primarySpeciesAgeToBreastHeight; + if (getBank().yearsToBreastHeight[primarySpeciesIndex] <= 0.0) { + getBank().yearsToBreastHeight[primarySpeciesIndex] = primarySpeciesAgeToBreastHeight; } arePrimarySpeciesDetailsSet = true; @@ -376,105 +318,56 @@ public void updatePrimarySpeciesDetailsAfterGrowth(float newPrimarySpeciesDomina // primarySpeciesAgeToBreastHeight of course doesn't change. } - public void setCompatibilityVariableDetails( - MatrixMap3[] cvVolume, - MatrixMap2[] cvBasalArea, - MatrixMap2[] cvQuadraticMeanDiameter, - Map[] cvPrimaryLayerSmall - ) { - if (areCompatibilityVariablesSet) { - throw new IllegalStateException(COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY); - } - - this.cvVolume = cvVolume; - this.cvBasalArea = cvBasalArea; - this.cvQuadraticMeanDiameter = cvQuadraticMeanDiameter; - this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; - - areCompatibilityVariablesSet = true; - } - /** * CVADJ1 - adjust the values of the compatibility variables after one year of growth. */ public void updateCompatibilityVariablesAfterGrowth() { - var compVarAdjustments = fps.fcm.getCompVarAdjustments(); + var compVarAdjustments = getParent().getControlMap().getCompVarAdjustments(); for (int i : getIndices()) { for (UtilizationClassVariable sucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - cvPrimaryLayerSmall[i].put( + getCvPrimaryLayerSmall()[i].put( sucv, - cvPrimaryLayerSmall[i].get(sucv) * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) + getCvPrimaryLayerSmall()[i].get(sucv) + * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) ); } for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - cvBasalArea[i].put( + getCvBasalArea()[i].put( uc, LayerType.PRIMARY, - cvBasalArea[i].get(uc, LayerType.PRIMARY) + getCvBasalArea()[i].get(uc, LayerType.PRIMARY) * compVarAdjustments.getValue(uc, UtilizationClassVariable.BASAL_AREA) ); - cvQuadraticMeanDiameter[i].put( + getCvQuadraticMeanDiameter()[i].put( uc, LayerType.PRIMARY, - cvQuadraticMeanDiameter[i].get(uc, LayerType.PRIMARY) + getCvQuadraticMeanDiameter()[i].get(uc, LayerType.PRIMARY) * compVarAdjustments.getValue(uc, UtilizationClassVariable.QUAD_MEAN_DIAMETER) ); for (UtilizationClassVariable vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { - cvVolume[i].put( + getCvVolume()[i].put( uc, vv, LayerType.PRIMARY, - cvVolume[i].get(uc, vv, LayerType.PRIMARY) * compVarAdjustments.getVolumeValue(uc, vv) + getCvVolume()[i].get(uc, vv, LayerType.PRIMARY) * compVarAdjustments.getVolumeValue(uc, vv) ); } } } } - public float getCVVolume( - int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable, LayerType layerType - ) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_VOLUMES); - } - - return cvVolume[speciesIndex].get(uc, volumeVariable, layerType); - } - - public float getCVBasalArea(int speciesIndex, UtilizationClass uc, LayerType layerType) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvBasalArea[speciesIndex].get(uc, layerType); - } - - public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc, LayerType layerType) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvQuadraticMeanDiameter[speciesIndex].get(uc, layerType); - } - - public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvPrimaryLayerSmall[speciesIndex].get(variable); - } - - VdypLayer updateLayerFromBank() { + @Override + protected VdypLayer updateLayerFromBank() { - VdypLayer updatedLayer = bank.buildLayerFromBank(); + VdypLayer updatedLayer = getBank().buildLayerFromBank(); - if (layerType.equals(LayerType.PRIMARY)) { + if (getLayerType().equals(LayerType.PRIMARY)) { // Inject the compatibility variable values. for (int i = 1; i < getNSpecies() + 1; i++) { - VdypSpecies species = updatedLayer.getSpeciesBySp0(bank.speciesNames[i]); + VdypSpecies species = updatedLayer.getSpeciesBySp0(getBank().speciesNames[i]); species.setCompatibilityVariables( - cvVolume[i], cvBasalArea[i], cvQuadraticMeanDiameter[i], cvPrimaryLayerSmall[i] + getCvVolume()[i], getCvBasalArea()[i], getCvQuadraticMeanDiameter()[i], + getCvPrimaryLayerSmall()[i] ); } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index 5ed660c63..33432f39f 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -142,7 +142,8 @@ public ForwardProcessingEngine(Map controlMap, Optional veteranLayer = Optional .ofNullable(fps.getCurrentPolygon().getLayers().get(LayerType.VETERAN)); @@ -211,11 +212,11 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int // SCINXSET if (lastStepInclusive.ge(ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES)) { - calculateMissingSiteCurves(plps, fps.fcm.getSiteCurveMap()); + calculateMissingSiteCurves(plps, fps.getControlMap().getSiteCurveMap()); fps.getVeteranLayerProcessingState().ifPresent(vlps -> { assert veteranLayer.isPresent(); - calculateMissingSiteCurves(vlps, fps.fcm.getSiteCurveMap()); + calculateMissingSiteCurves(vlps, fps.getControlMap().getSiteCurveMap()); }); } @@ -239,7 +240,7 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int // VHDOM1 METH_H = 2, METH_A = 2, METH_SI = 2 if (lastStepInclusive.ge(ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX)) { - calculateDominantHeightAgeSiteIndex(plps, fps.fcm.getHl1Coefficients()); + calculateDominantHeightAgeSiteIndex(plps, fps.getControlMap().getHl1Coefficients()); } // CVSET1 @@ -253,10 +254,10 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int writeCurrentPolygon(startingYear, startingYear, stoppingYearInclusive); - boolean doRecalculateGroupsPriorToOutput = fps.fcm.getDebugSettings() + boolean doRecalculateGroupsPriorToOutput = fps.getControlMap().getDebugSettings() .getValue(ForwardDebugSettings.Vars.SPECIES_DYNAMICS_1) != 1 && plps.getNSpecies() > 1; - boolean doRecalculateGroups = fps.fcm.getForwardControlVariables() + boolean doRecalculateGroups = fps.getControlMap().getForwardControlVariables() .getControlVariable(ControlVariable.UPDATE_DURING_GROWTH_6) >= 1; int currentYear = startingYear + 1; @@ -278,7 +279,7 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int // If update-during-growth is set, update the context prior to output if (doRecalculateGroupsPriorToOutput) { calculateCoverages(plps); - calculateDominantHeightAgeSiteIndex(plps, fps.fcm.getHl1Coefficients()); + calculateDominantHeightAgeSiteIndex(plps, fps.getControlMap().getHl1Coefficients()); } // Write polygon (both primary and veteran layers) to output @@ -288,7 +289,7 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int // context now. if (!doRecalculateGroupsPriorToOutput && doRecalculateGroups) { calculateCoverages(plps); - calculateDominantHeightAgeSiteIndex(plps, fps.fcm.getHl1Coefficients()); + calculateDominantHeightAgeSiteIndex(plps, fps.getControlMap().getHl1Coefficients()); } currentYear += 1; @@ -309,7 +310,7 @@ private void executeForwardAlgorithm(ForwardExecutionStep lastStepInclusive, int * @throws ProcessingException */ private void grow( - LayerProcessingState lps, int currentYear, Optional veteranLayer, + ForwardLayerProcessingState lps, int currentYear, Optional veteranLayer, ForwardExecutionStep lastStepInclusive ) throws ProcessingException { @@ -362,7 +363,7 @@ private void grow( if (ForwardExecutionStep.GROW_3_LAYER_DQDELTA.eq(lastStepInclusive)) return; - int debugSetting9Value = fps.fcm.getDebugSettings() + int debugSetting9Value = fps.getControlMap().getDebugSettings() .getValue(ForwardDebugSettings.Vars.DO_LIMIT_BA_WHEN_DQ_LIMITED_9); if (debugSetting9Value == 1 && wasDqGrowthLimitApplied.get() /* is true */) { // Limit BA growth if DQ hit limit. @@ -399,7 +400,8 @@ private void grow( // and trees-per-hectare, using one of several options: "full species dynamics", // "partial species dynamics" or "no species dynamics." - int debugSetting1Value = fps.fcm.getDebugSettings().getValue(ForwardDebugSettings.Vars.SPECIES_DYNAMICS_1); + int debugSetting1Value = fps.getControlMap().getDebugSettings() + .getValue(ForwardDebugSettings.Vars.SPECIES_DYNAMICS_1); boolean wasSolutionFound = false; if (debugSetting1Value == 2) { @@ -546,11 +548,11 @@ private void grow( VdypLayer primaryLayer = fps.updatePolygon().getLayers().get(LayerType.PRIMARY); VolumeComputeMode volumeComputationMode = VolumeComputeMode.BY_UTIL_WITH_WHOLE_STEM_BY_SPEC; - int controlVariable3Value = fps.fcm.getForwardControlVariables() + int controlVariable3Value = fps.getControlMap().getForwardControlVariables() .getControlVariable(ControlVariable.COMPAT_VAR_APPLICATION_3); CompatibilityVariableMode compatibilityVariableMode = CompatibilityVariableMode .getByInteger(controlVariable3Value); - lps.getFps().computers.computeUtilizationComponentsPrimary( + lps.getParent().getComputers().computeUtilizationComponentsPrimary( lps.getBecZone(), primaryLayer, volumeComputationMode, compatibilityVariableMode ); @@ -615,7 +617,7 @@ private void grow( return; } - private void updateVeteranSpeciesAges(LayerProcessingState vlps) { + private void updateVeteranSpeciesAges(ForwardLayerProcessingState vlps) { for (int i : vlps.getIndices()) { vlps.getBank().ageTotals[i] += 1; @@ -648,7 +650,7 @@ private void writeCheckpoint(int year) { */ void growUsingNoSpeciesDynamics(float baChangeRate, float tphChangeRate) { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); for (int i : lps.getIndices()) { @@ -718,7 +720,7 @@ boolean growUsingPartialSpeciesDynamics( float baStart, float baDelta, float dqStart, float dqDelta, float tphStart, float[] hlStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); Region polygonRegion = fps.getCurrentBecZone().getRegion(); @@ -746,7 +748,7 @@ boolean growUsingPartialSpeciesDynamics( tphEndAll[0] = BaseAreaTreeDensityDiameter.treesPerHectare(baEndAll[0], dqEndAll[0]); Map basalAreaPercentagesPerSpecies = new HashMap<>(); - for (String spAlias : fps.fcm.getGenusDefinitionMap().getAllGeneraAliases()) { + for (String spAlias : fps.getControlMap().getGenusDefinitionMap().getAllGeneraAliases()) { basalAreaPercentagesPerSpecies.put(spAlias, 0.0f); } for (int i : lps.getIndices()) { @@ -755,12 +757,12 @@ boolean growUsingPartialSpeciesDynamics( for (int i : lps.getIndices()) { - spDqStartEsts[i] = fps.estimators.estimateQuadMeanDiameterForSpecies( + spDqStartEsts[i] = fps.getEstimators().estimateQuadMeanDiameterForSpecies( bank.speciesNames[i], hlStart[i], bank.quadMeanDiameters[i][UC_ALL_INDEX], basalAreaPercentagesPerSpecies, lps.getBecZone().getRegion(), dqStart, baStart, tphStart, hlStart[0] ); - spDqEndEsts[i] = fps.estimators.estimateQuadMeanDiameterForSpecies( + spDqEndEsts[i] = fps.getEstimators().estimateQuadMeanDiameterForSpecies( bank.speciesNames[i], bank.loreyHeights[i][UC_ALL_INDEX], dqEndAll[0], basalAreaPercentagesPerSpecies, lps.getBecZone().getRegion(), dqEndAll[0], baEndAll[0], tphEndAll[0], bank.loreyHeights[0][UC_ALL_INDEX] @@ -791,7 +793,8 @@ boolean growUsingPartialSpeciesDynamics( continue; } - var sizeLimits = fps.estimators.getLimitsForHeightAndDiameter(bank.speciesNames[i], polygonRegion); + var sizeLimits = fps.getEstimators() + .getLimitsForHeightAndDiameter(bank.speciesNames[i], polygonRegion); var spDqMax = sizeLimits.quadMeanDiameterMaximum(); float spDqStart = bank.quadMeanDiameters[i][UC_ALL_INDEX]; @@ -1018,7 +1021,7 @@ void growUsingFullSpeciesDynamics( float baStart, float baDelta, float dqStart, float dqDelta, float tphStart, float lhStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float spBaEnd[] = new float[lps.getNSpecies() + 1]; @@ -1235,7 +1238,7 @@ void growUsingFullSpeciesDynamics( * @return as described */ private ComponentSizeLimits getComponentSizeLimits(String genusAlias, Region region) { - return fps.fcm.getComponentSizeLimits().get(genusAlias, region); + return fps.getControlMap().getComponentSizeLimits().get(genusAlias, region); } /** @@ -1255,10 +1258,11 @@ private float calculateQuadMeanDiameterDeltaForPrimarySpecies( float dqStart, float dqDelta, float pspDqStart, float lhStart, float pspLhStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); int pspStratumNumber = lps.getPrimarySpeciesStratumNumber(); - ModelCoefficients mc = fps.fcm.getPrimarySpeciesQuadMeanDiameterGrowthCoefficients().get(pspStratumNumber); + ModelCoefficients mc = fps.getControlMap().getPrimarySpeciesQuadMeanDiameterGrowthCoefficients() + .get(pspStratumNumber); if (mc == null) { throw new ProcessingException( @@ -1290,16 +1294,17 @@ private float calculateQuadMeanDiameterDeltaForNonPrimarySpecies( int speciesIndex, float dqStart, float dqDelta, float spDqStart, float lhStart, float spLhStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); String speciesName = lps.getBank().speciesNames[speciesIndex]; int pspStratumNumber = lps.getPrimarySpeciesStratumNumber(); - var modelCoefficientsOpt = fps.fcm.getNonPrimarySpeciesQuadMeanDiameterGrowthCoefficients() + var modelCoefficientsOpt = fps.getControlMap().getNonPrimarySpeciesQuadMeanDiameterGrowthCoefficients() .get(speciesName, pspStratumNumber); if (modelCoefficientsOpt.isEmpty()) { - modelCoefficientsOpt = fps.fcm.getNonPrimarySpeciesQuadMeanDiameterGrowthCoefficients().get(speciesName, 0); + modelCoefficientsOpt = fps.getControlMap().getNonPrimarySpeciesQuadMeanDiameterGrowthCoefficients() + .get(speciesName, 0); } if (modelCoefficientsOpt.isEmpty()) { @@ -1373,7 +1378,7 @@ private float growBasalAreaForNonPrimarySpecies( float spLhStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); if (spBaStart <= 0.0f || spBaStart >= baStart) { throw new ProcessingException( @@ -1387,11 +1392,12 @@ private float growBasalAreaForNonPrimarySpecies( int pspStratumNumber = lps.getPrimarySpeciesStratumNumber(); - var coe = fps.fcm.getNonPrimarySpeciesBasalAreaGrowthCoefficients(); + var coe = fps.getControlMap().getNonPrimarySpeciesBasalAreaGrowthCoefficients(); var modelCoefficientsOpt = coe.get(speciesName, pspStratumNumber); if (modelCoefficientsOpt.isEmpty()) { - modelCoefficientsOpt = fps.fcm.getNonPrimarySpeciesBasalAreaGrowthCoefficients().get(speciesName, 0); + modelCoefficientsOpt = fps.getControlMap().getNonPrimarySpeciesBasalAreaGrowthCoefficients() + .get(speciesName, 0); } if (modelCoefficientsOpt.isEmpty()) { @@ -1439,7 +1445,7 @@ private float growBasalAreaForPrimarySpecies( float baStart, float baDelta, float pspBaStart, float dhStart, float pspYabhStart, float pspLhStart ) throws ProcessingException { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); float pspBaDelta; @@ -1447,7 +1453,8 @@ private float growBasalAreaForPrimarySpecies( if (spToAllProportionStart <= 0.999f) { var psStratumNumber = lps.getPrimarySpeciesStratumNumber(); - ModelCoefficients mc = fps.fcm.getPrimarySpeciesBasalAreaGrowthCoefficients().get(psStratumNumber); + ModelCoefficients mc = fps.getControlMap().getPrimarySpeciesBasalAreaGrowthCoefficients() + .get(psStratumNumber); int model = mc.getModel(); var a0 = mc.getCoefficients().getCoe(1); var a1 = mc.getCoefficients().getCoe(2); @@ -1484,7 +1491,7 @@ private float growBasalAreaForPrimarySpecies( * * @throws ProcessingException */ - private void calculateSmallComponentYields(LayerProcessingState lps) throws ProcessingException { + private void calculateSmallComponentYields(ForwardLayerProcessingState lps) throws ProcessingException { Bank bank = lps.getBank(); @@ -1532,7 +1539,7 @@ private void calculateSmallComponentYields(LayerProcessingState lps) throws Proc // EMP086 float meanVolumeSmall = meanVolumeSmall(speciesName, spLhSmall, spDqSmall); // VMEANSMs - int controlVar3Value = fps.fcm.getForwardControlVariables() + int controlVar3Value = fps.getControlMap().getForwardControlVariables() .getControlVariable(ControlVariable.COMPAT_VAR_APPLICATION_3); if (controlVar3Value >= 1 /* apply compatibility variables */) { @@ -1596,7 +1603,8 @@ private void calculateSmallComponentYields(LayerProcessingState lps) throws Proc * @param pspLhStart primary species Lorey height at end */ void growLoreyHeights( - LayerProcessingState lps, float dhStart, float dhEnd, float pspTphStart, float pspTphEnd, float pspLhStart + ForwardLayerProcessingState lps, float dhStart, float dhEnd, float pspTphStart, float pspTphEnd, + float pspLhStart ) { Bank bank = lps.getBank(); @@ -1604,12 +1612,12 @@ void growLoreyHeights( float pspLhEndEstimate = estimatePrimarySpeciesLoreyHeight(dhEnd, pspTphEnd); float primaryF = (pspLhStart - 1.3f) / (pspLhStartEstimate - 1.3f); - float primaryLhAdjustment = fps.fcm.getCompVarAdjustments().getLoreyHeightPrimaryParam(); + float primaryLhAdjustment = fps.getControlMap().getCompVarAdjustments().getLoreyHeightPrimaryParam(); primaryF = 1.0f + (primaryF - 1.0f) * primaryLhAdjustment; float pspLhEnd = 1.3f + (pspLhEndEstimate - 1.3f) * primaryF; - int debugSetting8Value = fps.fcm.getDebugSettings() + int debugSetting8Value = fps.getControlMap().getDebugSettings() .getValue(ForwardDebugSettings.Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8); int primarySpeciesIndex = fps.getPrimaryLayerProcessingState().getPrimarySpeciesIndex(); @@ -1619,7 +1627,7 @@ void growLoreyHeights( pspLhEnd = bank.loreyHeights[primarySpeciesIndex][UC_ALL_INDEX]; } - float nonPrimaryLhAdjustment = fps.fcm.getCompVarAdjustments().getLoreyHeightOther(); + float nonPrimaryLhAdjustment = fps.getControlMap().getCompVarAdjustments().getLoreyHeightOther(); for (int i : lps.getIndices()) { if (i != primarySpeciesIndex && bank.basalAreas[i][UC_ALL_INDEX] > 0.0f) { @@ -1648,7 +1656,7 @@ private float estimatePrimarySpeciesLoreyHeight(float dh, float pspTph) { String primarySpeciesAlias = fps.getPrimaryLayerProcessingState().getPrimarySpeciesAlias(); Region polygonRegion = fps.getPrimaryLayerProcessingState().getBecZone().getRegion(); - var coefficients = fps.fcm.getLoreyHeightPrimarySpeciesEquationP1Coefficients(); + var coefficients = fps.getControlMap().getLoreyHeightPrimarySpeciesEquationP1Coefficients(); float a0 = coefficients.get(primarySpeciesAlias, polygonRegion).getCoe(1); float a1 = coefficients.get(primarySpeciesAlias, polygonRegion).getCoe(2); @@ -1669,7 +1677,7 @@ private float estimatePrimarySpeciesLoreyHeight(float dh, float pspTph) { * @return as described */ private float estimateNonPrimarySpeciesLoreyHeight(int speciesIndex, float dh, float pspLoreyHeight) { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float spLh; @@ -1679,7 +1687,7 @@ private float estimateNonPrimarySpeciesLoreyHeight(int speciesIndex, float dh, f String speciesAlias = bank.speciesNames[speciesIndex]; Region region = lps.getBecZone().getRegion(); - var coefficients = fps.fcm.getLoreyHeightNonPrimaryCoefficients(); + var coefficients = fps.getControlMap().getLoreyHeightNonPrimaryCoefficients(); var configuredLhCoefficients = coefficients.get(speciesAlias, primarySpeciesAlias, region); var lhCoefficients = configuredLhCoefficients.orElseGet(() -> NonprimaryHLCoefficients.getDefault()); @@ -1726,7 +1734,7 @@ float calculateQuadMeanDiameterDelta( float[] speciesProportionsByBasalArea = getSpeciesProportionsByBasalAreaAtStartOfYear(); - var dqYieldCoefficients = fps.fcm.getQuadMeanDiameterYieldCoefficients(); + var dqYieldCoefficients = fps.getControlMap().getQuadMeanDiameterYieldCoefficients(); var decayBecZoneAlias = becZone.getDecayBec().getAlias(); Coefficients coefficientsWeightedBySpeciesAndDecayBec = Coefficients.empty(6, 0); for (int i = 0; i < 6; i++) { @@ -1742,9 +1750,9 @@ float calculateQuadMeanDiameterDelta( float dqUpperBound = growQuadraticMeanDiameterUpperBound(); float dqLimit = Math.max(dqUpperBound, dqStart); - int debugVariable2Value = fps.fcm.getDebugSettings().getValue(Vars.MAX_BREAST_HEIGHT_AGE_2); + int debugVariable2Value = fps.getControlMap().getDebugSettings().getValue(Vars.MAX_BREAST_HEIGHT_AGE_2); - float dqYieldStart = fps.estimators.estimateQuadMeanDiameterYield( + float dqYieldStart = fps.getEstimators().estimateQuadMeanDiameterYield( coefficientsWeightedBySpeciesAndDecayBec, debugVariable2Value, dhStart, pspYabhStart, veteranBaStart, dqLimit ); @@ -1752,15 +1760,16 @@ float calculateQuadMeanDiameterDelta( float dhEnd = dhStart + dhDelta; float pspYabhEnd = pspYabhStart + 1.0f; - float dqYieldEnd = fps.estimators.estimateQuadMeanDiameterYield( + float dqYieldEnd = fps.getEstimators().estimateQuadMeanDiameterYield( coefficientsWeightedBySpeciesAndDecayBec, debugVariable2Value, dhEnd, pspYabhEnd, veteranBaEnd, dqLimit ); float dqYieldGrowth = dqYieldEnd - dqYieldStart; - int debugSetting6Value = fps.fcm.getDebugSettings().getValue(ForwardDebugSettings.Vars.DQ_GROWTH_MODEL_6); + int debugSetting6Value = fps.getControlMap().getDebugSettings() + .getValue(ForwardDebugSettings.Vars.DQ_GROWTH_MODEL_6); - var growthFaitDetails = fps.fcm.getQuadMeanDiameterGrowthFiatDetails().get(becZone.getRegion()); + var growthFaitDetails = fps.getControlMap().getQuadMeanDiameterGrowthFiatDetails().get(becZone.getRegion()); Optional dqGrowthFiat = Optional.empty(); if (debugSetting6Value != 1) { @@ -1843,7 +1852,7 @@ private float calculateQuadMeanDiameterGrowthEmpirical( ) { // Compute the growth in quadratic mean diameter - var dqGrowthEmpiricalCoefficients = fps.fcm.getQuadMeanDiameterGrowthEmpiricalCoefficients(); + var dqGrowthEmpiricalCoefficients = fps.getControlMap().getQuadMeanDiameterGrowthEmpiricalCoefficients(); Integer stratumNumber = fps.getPrimaryLayerProcessingState().getPrimarySpeciesStratumNumber(); var firstSpeciesDqGrowthCoe = dqGrowthEmpiricalCoefficients.get(stratumNumber); @@ -1867,7 +1876,7 @@ private float calculateQuadMeanDiameterGrowthEmpirical( // Compute min/max growth in quadratic mean diameter - Map quadMeanDiameterGrowthEmpiricalLimits = fps.fcm + Map quadMeanDiameterGrowthEmpiricalLimits = fps.getControlMap() .getQuadMeanDiameterGrowthEmpiricalLimits(); float[] dqDeltaLimits = new float[8]; for (int i = 0; i < 8; i++) { @@ -1894,7 +1903,7 @@ private float calculateQuadMeanDiameterGrowthEmpirical( */ private float[] getSpeciesProportionsByBasalAreaAtStartOfYear() { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float[] speciesProportionsByBasalArea = new float[lps.getNSpecies() + 1]; @@ -1922,13 +1931,13 @@ float calculateBasalAreaDelta( float pspYabhStart, float pspDhStart, float baStart, Optional veteranLayerBaStart, float dhDelta ) throws StandProcessingException { - ForwardDebugSettings debugSettings = fps.fcm.getDebugSettings(); - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardDebugSettings debugSettings = fps.getControlMap().getDebugSettings(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float[] speciesProportionsByBasalArea = getSpeciesProportionsByBasalAreaAtStartOfYear(); - var baYieldCoefficients = fps.fcm.getBasalAreaYieldCoefficients(); + var baYieldCoefficients = fps.getControlMap().getBasalAreaYieldCoefficients(); var becZoneAlias = fps.getPrimaryLayerProcessingState().getBecZone().getAlias(); Coefficients estimateBasalAreaYieldCoefficients = Coefficients.empty(7, 0); for (int i = 0; i <= 6; i++) { @@ -1948,9 +1957,9 @@ float calculateBasalAreaDelta( var baUpperBound = growBasalAreaUpperBound(); boolean isFullOccupancy = true; - int debugSetting2Value = fps.fcm.getDebugSettings().getValue(Vars.MAX_BREAST_HEIGHT_AGE_2); + int debugSetting2Value = fps.getControlMap().getDebugSettings().getValue(Vars.MAX_BREAST_HEIGHT_AGE_2); - float baYieldStart = fps.estimators.estimateBaseAreaYield( + float baYieldStart = fps.getEstimators().estimateBaseAreaYield( estimateBasalAreaYieldCoefficients, debugSetting2Value, pspDhStart, pspYabhStart, veteranLayerBaStart, isFullOccupancy, baUpperBound ); @@ -1958,12 +1967,12 @@ float calculateBasalAreaDelta( float pspDhEnd = pspDhStart + dhDelta; float pspYabhEnd = pspYabhStart + 1.0f; - float baYieldEnd = fps.estimators.estimateBaseAreaYield( + float baYieldEnd = fps.getEstimators().estimateBaseAreaYield( estimateBasalAreaYieldCoefficients, debugSetting2Value, pspDhEnd, pspYabhEnd, veteranLayerBaStart, isFullOccupancy, baUpperBound ); - var growthFaitDetails = fps.fcm.getBasalAreaGrowthFiatDetails() + var growthFaitDetails = fps.getControlMap().getBasalAreaGrowthFiatDetails() .get(fps.getPrimaryLayerProcessingState().getBecZone().getRegion()); var convergenceCoefficient = growthFaitDetails.calculateCoefficient(pspYabhStart); @@ -2043,10 +2052,10 @@ private float calculateBasalAreaGrowthEmpirical( pspYabhStart = 999.0f; } - var basalAreaGrowthEmpiricalCoefficients = fps.fcm.getBasalAreaGrowthEmpiricalCoefficients(); + var basalAreaGrowthEmpiricalCoefficients = fps.getControlMap().getBasalAreaGrowthEmpiricalCoefficients(); String becZoneAlias = fps.getPrimaryLayerProcessingState().getBecZone().getAlias(); - String firstSpecies = fps.fcm.getGenusDefinitionMap().getByIndex(1).getAlias(); + String firstSpecies = fps.getControlMap().getGenusDefinitionMap().getByIndex(1).getAlias(); var firstSpeciesBaGrowthCoe = basalAreaGrowthEmpiricalCoefficients.get(becZoneAlias, firstSpecies); float b0 = firstSpeciesBaGrowthCoe.get(0); @@ -2104,18 +2113,19 @@ private float calculateBasalAreaGrowthEmpirical( */ private float growBasalAreaUpperBound() { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); - int debugSetting4Value = fps.fcm.getDebugSettings() + int debugSetting4Value = fps.getControlMap().getDebugSettings() .getValue(ForwardDebugSettings.Vars.PER_SPECIES_AND_REGION_MAX_BREAST_HEIGHT_4); if (debugSetting4Value > 0) { - var upperBoundsCoefficients = fps.fcm.getUpperBoundsCoefficients(); + var upperBoundsCoefficients = fps.getControlMap().getUpperBoundsCoefficients(); Region region = lps.getBecZone().getRegion(); int primarySpeciesIndex = lps.getPrimarySpeciesIndex(); return upperBoundsCoefficients.get(region, lps.getBank().speciesNames[primarySpeciesIndex], 1); } else { var primarySpeciesGroupNumber = lps.getPrimarySpeciesGroupNumber(); - return fps.fcm.getUpperBounds().get(primarySpeciesGroupNumber).getCoe(UpperBoundsParser.BA_INDEX); + return fps.getControlMap().getUpperBounds().get(primarySpeciesGroupNumber) + .getCoe(UpperBoundsParser.BA_INDEX); } } @@ -2124,19 +2134,20 @@ private float growBasalAreaUpperBound() { */ private float growQuadraticMeanDiameterUpperBound() { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); - int debugSetting4Value = fps.fcm.getDebugSettings() + int debugSetting4Value = fps.getControlMap().getDebugSettings() .getValue(ForwardDebugSettings.Vars.PER_SPECIES_AND_REGION_MAX_BREAST_HEIGHT_4); if (debugSetting4Value > 0) { - var upperBoundsCoefficients = fps.fcm.getUpperBoundsCoefficients(); + var upperBoundsCoefficients = fps.getControlMap().getUpperBoundsCoefficients(); Region region = lps.getBecZone().getRegion(); int primarySpeciesIndex = lps.getPrimarySpeciesIndex(); return upperBoundsCoefficients.get(region, lps.getBank().speciesNames[primarySpeciesIndex], 2); } else { var primarySpeciesGroupNumber = fps.getPrimaryLayerProcessingState().getPrimarySpeciesGroupNumber(); - return fps.fcm.getUpperBounds().get(primarySpeciesGroupNumber).getCoe(UpperBoundsParser.DQ_INDEX); + return fps.getControlMap().getUpperBounds().get(primarySpeciesGroupNumber) + .getCoe(UpperBoundsParser.DQ_INDEX); } } @@ -2155,7 +2166,7 @@ private float growQuadraticMeanDiameterUpperBound() { float calculateDominantHeightDelta(float spDhStart, int siteCurveNumber, float siStart, float yearsToBreastHeight) throws ProcessingException { - SiteCurveAgeMaximum scAgeMaximums = fps.fcm.getMaximumAgeBySiteCurveNumber().get(siteCurveNumber); + SiteCurveAgeMaximum scAgeMaximums = fps.getControlMap().getMaximumAgeBySiteCurveNumber().get(siteCurveNumber); Region region = fps.getPrimaryLayerProcessingState().getBank().getBecZone().getRegion(); if (siteCurveNumber == VdypEntity.MISSING_INTEGER_VALUE) { @@ -2347,7 +2358,7 @@ private void writeCurrentPolygon(int startYear, int currentYear, int endYear) th logger.info("Writing polygon {} for year {}", polygon, currentYear); - int controlVariable4Value = fps.fcm.getForwardControlVariables() + int controlVariable4Value = fps.getControlMap().getForwardControlVariables() .getControlVariable(ControlVariable.OUTPUT_FILES_4); switch (controlVariable4Value) { @@ -2411,7 +2422,7 @@ void setCompatibilityVariables() throws ProcessingException { Coefficients aAdjust = new Coefficients(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, 1); - var growthDetails = fps.fcm.getForwardControlVariables(); + var growthDetails = fps.getControlMap().getForwardControlVariables(); var lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); @@ -2481,7 +2492,7 @@ void setCompatibilityVariables() throws ProcessingException { if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { // EMP094 - fps.estimators.estimateNetDecayAndWasteVolume( + fps.getEstimators().estimateNetDecayAndWasteVolume( lps.getBecZone().getRegion(), uc, aAdjust, bank.speciesNames[s], spLoreyHeight_All, quadMeanDiameters, closeUtilizationVolumes, closeUtilizationVolumesNetOfDecay, closeUtilizationVolumesNetOfDecayAndWaste @@ -2505,7 +2516,7 @@ void setCompatibilityVariables() throws ProcessingException { // EMP093 int decayGroup = lps.getDecayEquationGroups()[s]; - fps.estimators.estimateNetDecayVolume( + fps.getEstimators().estimateNetDecayVolume( bank.speciesNames[s], lps.getBecZone().getRegion(), uc, aAdjust, decayGroup, lps.getPrimarySpeciesAgeAtBreastHeight(), quadMeanDiameters, closeUtilizationVolumes, closeUtilizationVolumesNetOfDecay @@ -2526,7 +2537,7 @@ void setCompatibilityVariables() throws ProcessingException { // EMP092 int volumeGroup = lps.getVolumeEquationGroups()[s]; - fps.estimators.estimateCloseUtilizationVolume( + fps.getEstimators().estimateCloseUtilizationVolume( uc, aAdjust, volumeGroup, spLoreyHeight_All, quadMeanDiameters, wholeStemVolumes, closeUtilizationVolumes ); @@ -2541,12 +2552,12 @@ void setCompatibilityVariables() throws ProcessingException { int primarySpeciesVolumeGroup = lps.getVolumeEquationGroups()[s]; float primarySpeciesQMDAll = bank.quadMeanDiameters[s][UC_ALL_INDEX]; - var wholeStemVolume = bank.treesPerHectare[s][UC_ALL_INDEX] * fps.estimators + var wholeStemVolume = bank.treesPerHectare[s][UC_ALL_INDEX] * fps.getEstimators() .estimateWholeStemVolumePerTree(primarySpeciesVolumeGroup, spLoreyHeight_All, primarySpeciesQMDAll); wholeStemVolumes.setCoe(UC_ALL_INDEX, wholeStemVolume); - fps.estimators.estimateWholeStemVolume( + fps.getEstimators().estimateWholeStemVolume( UtilizationClass.ALL, 0.0f, primarySpeciesVolumeGroup, spLoreyHeight_All, quadMeanDiameters, basalAreas, wholeStemVolumes ); @@ -2563,9 +2574,10 @@ void setCompatibilityVariables() throws ProcessingException { cvVolume[s].put(uc, UtilizationClassVariable.WHOLE_STEM_VOL, LayerType.PRIMARY, adjustment); } - fps.estimators.estimateQuadMeanDiameterByUtilization(lps.getBecZone(), quadMeanDiameters, genusName); + fps.getEstimators().estimateQuadMeanDiameterByUtilization(lps.getBecZone(), quadMeanDiameters, genusName); - fps.estimators.estimateBaseAreaByUtilization(lps.getBecZone(), quadMeanDiameters, basalAreas, genusName); + fps.getEstimators() + .estimateBaseAreaByUtilization(lps.getBecZone(), quadMeanDiameters, basalAreas, genusName); // Calculate trees-per-hectare per utilization treesPerHectare.setCoe(UtilizationClass.ALL.index, bank.treesPerHectare[s][UC_ALL_INDEX]); @@ -2699,9 +2711,9 @@ void setCompatibilityVariables() throws ProcessingException { * @return as described */ private float smallComponentProbability(String speciesAlias, float loreyHeight, Region region) { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); - Coefficients coe = fps.fcm.getSmallComponentProbabilityCoefficients().get(speciesAlias); + Coefficients coe = fps.getControlMap().getSmallComponentProbabilityCoefficients().get(speciesAlias); // EQN 1 in IPSJF118.doc @@ -2734,7 +2746,7 @@ private float smallComponentProbability(String speciesAlias, float loreyHeight, private float calculateSmallComponentConditionalExpectedBasalArea( String sp0Name, float spBaEnd, float spLhEnd, Region region ) { - Coefficients coe = fps.fcm.getSmallComponentBasalAreaCoefficients().get(sp0Name); + Coefficients coe = fps.getControlMap().getSmallComponentBasalAreaCoefficients().get(sp0Name); float a0 = coe.getCoe(1); float a1 = coe.getCoe(2); @@ -2755,7 +2767,7 @@ private float calculateSmallComponentConditionalExpectedBasalArea( // EMP082 private float smallComponentQuadMeanDiameter(String speciesName, float loreyHeight) { - Coefficients coe = fps.fcm.getSmallComponentQuadMeanDiameterCoefficients().get(speciesName); + Coefficients coe = fps.getControlMap().getSmallComponentQuadMeanDiameterCoefficients().get(speciesName); // EQN 5 in IPSJF118.doc @@ -2772,7 +2784,7 @@ private float smallComponentLoreyHeight( String speciesName, float speciesLoreyHeight_All, float quadMeanDiameterSpecSmall, float speciesQuadMeanDiameter_All ) { - Coefficients coe = fps.fcm.getSmallComponentLoreyHeightCoefficients().get(speciesName); + Coefficients coe = fps.getControlMap().getSmallComponentLoreyHeightCoefficients().get(speciesName); // EQN 1 in IPSJF119.doc @@ -2787,7 +2799,7 @@ private float smallComponentLoreyHeight( // EMP086 private float meanVolumeSmall(String speciesName, float spHlSmall, float spDqSmall) { - Coefficients coe = fps.fcm.getSmallComponentWholeStemVolumeCoefficients().get(speciesName); + Coefficients coe = fps.getControlMap().getSmallComponentWholeStemVolumeCoefficients().get(speciesName); // EQN 1 in IPSJF119.doc @@ -2854,7 +2866,7 @@ private static float calculateWholeStemVolume(float actualVolume, float basalAre * @throws ProcessingException */ static void calculateDominantHeightAgeSiteIndex( - LayerProcessingState lps, MatrixMap2 hl1Coefficients + ForwardLayerProcessingState lps, MatrixMap2 hl1Coefficients ) throws ProcessingException { Bank bank = lps.getBank(); @@ -2972,7 +2984,7 @@ static void calculateDominantHeightAgeSiteIndex( * * @param lps the current state of the processing of the polygon */ - static void estimateMissingYearsToBreastHeightValues(LayerProcessingState lps) { + static void estimateMissingYearsToBreastHeightValues(ForwardLayerProcessingState lps) { Bank bank = lps.getBank(); @@ -3034,7 +3046,7 @@ static void estimateMissingYearsToBreastHeightValues(LayerProcessingState lps) { * @param lps the bank in which the calculations are done. * @throws ProcessingException */ - static void estimateMissingSiteIndices(LayerProcessingState lps) throws ProcessingException { + static void estimateMissingSiteIndices(ForwardLayerProcessingState lps) throws ProcessingException { Bank bank = lps.getBank(); @@ -3144,7 +3156,7 @@ static void estimateMissingSiteIndices(LayerProcessingState lps) throws Processi * * @param state the bank in which the calculations are performed */ - static void calculateCoverages(LayerProcessingState lps) { + static void calculateCoverages(ForwardLayerProcessingState lps) { Bank bank = lps.getBank(); @@ -3173,7 +3185,7 @@ static void calculateCoverages(LayerProcessingState lps) { * @param lps the PolygonProcessingState to where the calculated curves are also to be */ static void calculateMissingSiteCurves( - LayerProcessingState lps, MatrixMap2 siteCurveMap + ForwardLayerProcessingState lps, MatrixMap2 siteCurveMap ) { Bank bank = lps.getBank(); @@ -3275,7 +3287,7 @@ private static void stopIfNoWork(ForwardProcessingState fps) throws ProcessingEx */ void determinePolygonRankings(Collection> speciesToCombine) { - LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); if (lps.getNSpecies() == 0) { @@ -3326,8 +3338,9 @@ void determinePolygonRankings(Collection> speciesToCombine) { String primarySpeciesName = bank.speciesNames[highestPercentageIndex]; String becZoneAlias = bank.getBecZone().getAlias(); - int defaultEquationGroup = fps.fcm.getDefaultEquationGroup().get(primarySpeciesName, becZoneAlias); - Optional equationModifierGroup = fps.fcm.getEquationModifierGroup() + int defaultEquationGroup = fps.getControlMap().getDefaultEquationGroup() + .get(primarySpeciesName, becZoneAlias); + Optional equationModifierGroup = fps.getControlMap().getEquationModifierGroup() .get(defaultEquationGroup, inventoryTypeGroup); basalAreaGroup1 = equationModifierGroup.orElse(defaultEquationGroup); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java index 95b3b2538..b492392b4 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java @@ -1,89 +1,30 @@ package ca.bc.gov.nrs.vdyp.forward; import java.util.Map; -import java.util.Optional; import ca.bc.gov.nrs.vdyp.application.ProcessingException; -import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; -import ca.bc.gov.nrs.vdyp.common.ComputationMethods; -import ca.bc.gov.nrs.vdyp.common.EstimationMethods; import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMapImpl; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; -public class ForwardProcessingState { - - /** The control map defining the context of the execution */ - public final ForwardResolvedControlMap fcm; - - /** The estimators instance used by this engine */ - final EstimationMethods estimators; - - /** The computation instance used by this engine */ - final ComputationMethods computers; - - /** The polygon on which the Processor is currently operating */ - private VdypPolygon polygon; - - /** The processing state of the primary layer of polygon */ - private LayerProcessingState plps; - - /** The processing state of the veteran layer of polygon */ - private Optional vlps; +public class ForwardProcessingState extends ProcessingState { public ForwardProcessingState(Map controlMap) throws ProcessingException { - this.fcm = new ForwardResolvedControlMapImpl(controlMap); - this.estimators = new EstimationMethods(this.fcm); - this.computers = new ComputationMethods(estimators, VdypApplicationIdentifier.VDYP_FORWARD); + super(controlMap); } - public void setPolygon(VdypPolygon polygon) throws ProcessingException { - - this.polygon = polygon; - - this.plps = new LayerProcessingState(this, polygon.getLayers().get(LayerType.PRIMARY)); - if (polygon.getLayers().containsKey(LayerType.VETERAN)) { - this.vlps = Optional.of(new LayerProcessingState(this, polygon.getLayers().get(LayerType.VETERAN))); - } else { - this.vlps = Optional.empty(); - } + @Override + protected ForwardResolvedControlMap resolveControlMap(Map controlMap) { + return new ForwardResolvedControlMapImpl(controlMap); } - /** @return the current polygon */ - public VdypPolygon getCurrentPolygon() { - return polygon; + @Override + protected ForwardLayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) + throws ProcessingException { + return new ForwardLayerProcessingState(this, polygon, polygon.getLayers().get(LayerType.PRIMARY)); } - /** @return the compact form of the current polygon's identifier. Shortcut. */ - public String getCompactPolygonIdentifier() { - return polygon.getPolygonIdentifier().toStringCompact(); - } - - /** @return the starting year of the current polygon. Shortcut. */ - public int getCurrentStartingYear() { - return polygon.getPolygonIdentifier().getYear(); - } - - /** @return the bec zone of the current polygon. Shortcut. */ - public BecDefinition getCurrentBecZone() { - return polygon.getBiogeoclimaticZone(); - } - - public LayerProcessingState getPrimaryLayerProcessingState() { - return plps; - } - - public Optional getVeteranLayerProcessingState() { - return vlps; - } - - public VdypPolygon updatePolygon() { - - polygon.getLayers().put(LayerType.PRIMARY, plps.updateLayerFromBank()); - vlps.ifPresent(vlps -> polygon.getLayers().put(LayerType.VETERAN, vlps.updateLayerFromBank())); - - return polygon; - } } \ No newline at end of file diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java index 2a949f00a..c72762527 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessor.java @@ -100,7 +100,7 @@ public void process( var fpe = new ForwardProcessingEngine(controlMap, outputWriter); - var forwardDataStreamReader = new ForwardDataStreamReader(fpe.fps.fcm); + var forwardDataStreamReader = new ForwardDataStreamReader(fpe.fps.getControlMap()); // Fetch the next polygon to process. int nPolygonsProcessed = 0; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index bd6c22a8b..d18b70fdc 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -58,7 +58,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_10_COMPATIBILITY_VARS); // VDYP7 reports [], -9, -9, 35.473381, -9, -9) - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); assertThat( // VDYP7 reports BASAL_AREA = -2.13947629e-07, all others 0.0 lps.getCvPrimaryLayerSmall()[1], diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java index a4a307679..e62a30c32 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java @@ -102,7 +102,7 @@ void testDebugSettings3EqualsZeroPath() throws ProcessingException { float ba = 45.3864441f; float hdDelta = 0.173380271f; - fpe.fps.fcm.getDebugSettings().setValue(Vars.BASAL_AREA_GROWTH_MODEL_3, 0); + fpe.fps.getControlMap().getDebugSettings().setValue(Vars.BASAL_AREA_GROWTH_MODEL_3, 0); float gba = fpe.calculateBasalAreaDelta(yabh, hd, ba, Optional.empty(), hdDelta); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java index 4be94adbf..ff01ca7e8 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java @@ -60,7 +60,7 @@ void testMixedModel() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); float yabh = 54.0f; @@ -91,7 +91,7 @@ void testFiatOnlyModel() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); /* this value will force the fiat only calculations. */ float yabh = 54.0f; @@ -122,7 +122,7 @@ void testEmpiricalOnlyModel() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 1); /* this value will force the empirical only calculations. */ float yabh = 54.0f; @@ -153,7 +153,7 @@ void testMixedModelWithInterpolation() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); float yabh = 104.0f; /* this value will force interpolation. */ @@ -184,7 +184,7 @@ void testMinimumApplied() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); float yabh = 54.0f; @@ -215,7 +215,7 @@ void testLimitApplied() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); - ForwardDebugSettings debugSettings = fpe.fps.fcm.getDebugSettings(); + ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); float yabh = 54.0f; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java index db1e3addc..700f38546 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java @@ -56,7 +56,7 @@ void testStandardPath() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; float dhEnd = 35.473381f; @@ -84,7 +84,7 @@ void testDebug8Setting2Path() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; float dhEnd = 35.3f; @@ -92,7 +92,8 @@ void testDebug8Setting2Path() throws ProcessingException { float pspTphEnd = 287.107788f; float pspLhStart = 33.7439995f; - fpe.fps.fcm.getDebugSettings().setValue(ForwardDebugSettings.Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); + fpe.fps.getControlMap().getDebugSettings() + .setValue(ForwardDebugSettings.Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); fpe.growLoreyHeights(lps, dhStart, dhEnd, pspTphStart, pspTphEnd, pspLhStart); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java index be900892b..c161a88f7 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java @@ -56,7 +56,7 @@ void testStandardPath() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_5A_LH_EST); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baStart = 45.3864441f; float baDelta = 0.351852179f; @@ -109,12 +109,12 @@ void testGrowUsingNoSpeciesDynamics() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baChangeRate = 0.00775236264f; float tphChangeRate = 0.987927794f; - fpe.fps.fcm.getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 1); + fpe.fps.getControlMap().getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 1); fpe.growUsingNoSpeciesDynamics(baChangeRate, tphChangeRate); @@ -151,9 +151,9 @@ void testGrowUsingFullSpeciesDynamics() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); - fpe.fps.fcm.getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 0); + fpe.fps.getControlMap().getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 0); // Results are stored in bank.basalAreas[1..nSpecies] diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java index c15986d64..e9f5a7989 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java @@ -56,7 +56,7 @@ void testStandardPath() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_6_LAYER_TPH2); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); var calculatedLayerTph = lps.getBank().treesPerHectare[0][UtilizationClass.ALL.ordinal()]; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java index d244d436c..77bf8a4b6 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java @@ -56,7 +56,7 @@ void testStandardPath() throws ProcessingException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_7_LAYER_DQ2); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); var calculatedLayerDq = lps.getBank().quadMeanDiameters[0][UtilizationClass.ALL.ordinal()]; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java index b92d72702..dbdc9c0ec 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java @@ -55,10 +55,10 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.fps.fcm.getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 0); + fpe.fps.getControlMap().getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 0); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); var calculatedLayerDq = lps.getBank().quadMeanDiameters[0][UtilizationClass.ALL.ordinal()]; @@ -74,10 +74,10 @@ void testDebug8Setting2() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.fps.fcm.getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); + fpe.fps.getControlMap().getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); var calculatedLayerDq = lps.getBank().quadMeanDiameters[0][UtilizationClass.ALL.ordinal()]; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java index b809719e2..9e013d3c5 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java @@ -57,7 +57,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_9_SPECIES_PCT); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); // VDYP7 value is 0.892216682f, 11.5443392f, 64.3765259f, 13.3774729f, 9.80944252f assertThat( diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java index 1728a6ebf..e966d8a9a 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java @@ -38,9 +38,9 @@ void test() throws IOException, ResourceParseException, ProcessingException { ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - assertThat(fpe.fps.fcm.getBecLookup(), notNullValue()); - assertThat(fpe.fps.fcm.getGenusDefinitionMap(), notNullValue()); - assertThat(fpe.fps.fcm.getSiteCurveMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getBecLookup(), notNullValue()); + assertThat(fpe.fps.getControlMap().getGenusDefinitionMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getSiteCurveMap(), notNullValue()); int nPolygonsProcessed = 0; while (true) { @@ -70,7 +70,7 @@ void testFindPrimarySpecies() throws IOException, ResourceParseException, Proces ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); fpe.fps.setPolygon(polygon); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); ForwardProcessingEngine.calculateCoverages(lps); fpe.determinePolygonRankings(CommonData.PRIMARY_SPECIES_TO_COMBINE); @@ -84,7 +84,7 @@ void testFindPrimarySpecies() throws IOException, ResourceParseException, Proces { ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); fpe.fps.setPolygon(polygon); - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); var speciesToCombine = Arrays .asList(Arrays.asList(lps.getBank().speciesNames[3], lps.getBank().speciesNames[4])); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java index fa3f6d219..9007e5e42 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java @@ -45,7 +45,7 @@ void testSetCompatibilityVariables() throws ResourceParseException, IOException, // These values have been verified against the FORTRAN implementation, allowing for minor // platform-specific differences. - LayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); + ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); assertThat( lps.getVolumeEquationGroups(), diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessMultiplePolygonsBasicTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessMultiplePolygonsBasicTest.java index 25ad37cf2..df49e1814 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessMultiplePolygonsBasicTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessMultiplePolygonsBasicTest.java @@ -18,9 +18,9 @@ void test() throws IOException, ResourceParseException, ProcessingException { ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - assertThat(fpe.fps.fcm.getBecLookup(), notNullValue()); - assertThat(fpe.fps.fcm.getGenusDefinitionMap(), notNullValue()); - assertThat(fpe.fps.fcm.getSiteCurveMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getBecLookup(), notNullValue()); + assertThat(fpe.fps.getControlMap().getGenusDefinitionMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getSiteCurveMap(), notNullValue()); // Fetch the next polygon to process. int nPolygonsProcessed = 0; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java index 76a213b8e..b6deedbaf 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java @@ -24,9 +24,9 @@ void testOnePolygon() throws IOException, ResourceParseException, ProcessingExce ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - assertThat(fpe.fps.fcm.getBecLookup(), notNullValue()); - assertThat(fpe.fps.fcm.getGenusDefinitionMap(), notNullValue()); - assertThat(fpe.fps.fcm.getSiteCurveMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getBecLookup(), notNullValue()); + assertThat(fpe.fps.getControlMap().getGenusDefinitionMap(), notNullValue()); + assertThat(fpe.fps.getControlMap().getSiteCurveMap(), notNullValue()); int nPolygonsProcessed = 0; var polygon = forwardDataStreamReader.readNextPolygon(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java index e4b248c8d..d17a43b31 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/test/ForwardTestUtils.java @@ -106,7 +106,8 @@ public static Float[] toFloatArray(float[] af) { */ public static void setGrowthTargetYear(ForwardProcessingState fps, int targetYear) { try { - fps.fcm.getForwardControlVariables().setControlVariable(ControlVariable.GROW_TARGET_1, targetYear); + fps.getControlMap().getForwardControlVariables() + .setControlVariable(ControlVariable.GROW_TARGET_1, targetYear); } catch (ValueParseException e) { throw new RuntimeException(e); } From 07236050002b3d24ff2f1a2822bbecbf5e074b5c Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 19:05:32 -0800 Subject: [PATCH 34/45] Unit tests for deep equals matcher for layer --- .../ca/bc/gov/nrs/vdyp/model/VdypLayer.java | 8 + .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 1 - .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 46 +--- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 241 ++++++++++++++++++ .../forward/ForwardLayerProcessingState.java | 5 - 5 files changed, 254 insertions(+), 47 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java index 19947d12e..3230aa3d7 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java @@ -378,5 +378,13 @@ protected VdypSpecies buildSpecies(Consumer config) { this.primaryGenus(toCopy.getPrimaryGenus()); return super.copySpecies(toCopy, config); } + + @Override + public Builder copy(VdypLayer source) { + super.copy(source); + this.empiricalRelationshipParameterIndex(source.getEmpiricalRelationshipParameterIndex()); + return this; + } + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 0502281d0..2dd52b464 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -6,7 +6,6 @@ import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index b1318a066..8be2c3a05 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -926,48 +926,12 @@ protected boolean matchesSafely(VdypLayer item, Description mismatchDescription) } } } + for (var ucv : UtilizationClassVariable.values()) { + var expectedVector = ucv.get(expected); + var actualVector = ucv.get(item); - match = matchValue( - match, "getLoreyHeightByUtilization", expected.getLoreyHeightByUtilization(), - item.getLoreyHeightByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getBaseAreaByUtilization", expected.getBaseAreaByUtilization(), - item.getBaseAreaByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getQuadraticMeanDiameterByUtilization", - expected.getQuadraticMeanDiameterByUtilization(), item.getQuadraticMeanDiameterByUtilization(), - mismatchDescription - ); - match = matchValue( - match, "getTreesPerHectareByUtilization", expected.getTreesPerHectareByUtilization(), - item.getTreesPerHectareByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getWholeStemVolumeByUtilization", expected.getWholeStemVolumeByUtilization(), - item.getWholeStemVolumeByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getCloseUtilizationVolumeByUtilization", - expected.getCloseUtilizationVolumeByUtilization(), - item.getCloseUtilizationVolumeByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getCloseUtilizationVolumeNetOfDecayByUtilization", - expected.getCloseUtilizationVolumeNetOfDecayByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization", - expected.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(), mismatchDescription - ); - match = matchValue( - match, "getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization", - expected.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), - item.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(), mismatchDescription - ); + match = matchValue(match, ucv.getShortName(), expectedVector, actualVector, mismatchDescription); + } return match; } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 2a4ece6da..270a7d48e 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -32,6 +32,7 @@ import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSite; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; @@ -69,6 +70,246 @@ UtilizationVector mockHeightVector() { @Nested class deepEquals { + @Nested + class testVdypLayer { + VdypLayer expected; + Matcher unit; + + @BeforeEach + void setup() { + + Random rand = new Random(42); + var controlMap = TestUtils.loadControlMap(); + + // The numbers don't add up, we are just using them to test comparison + + expected = VdypLayer.build(lb -> { + lb.polygonIdentifier("Test", 2024); + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + unit = VdypMatchers.deepEquals(expected); + + } + + @Test + void testPass() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + + assertMatch(actual, unit); + } + + // Changing the key properties also causes mismatches on the children that share those key properties so use + // startsWith + + @Test + void testPolyIdDifferent() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + lb.polygonIdentifier("Different", 2025); + }); + assertMismatch( + actual, unit, + startsWith( + "PolygonIdentifier was but expected " + ) + ); + } + + @Test + void testLayerTypeDifferent() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + lb.layerType(LayerType.VETERAN); + }); + assertMismatch(actual, unit, startsWith("LayerType was but expected ")); + } + + @Test + void testEmpericalIndexDifferent() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + lb.empiricalRelationshipParameterIndex(23); + }); + assertMismatch( + actual, unit, + startsWith("EmpiricalRelationshipParameterIndex was but expected ") + ); + } + + @Test + void testInventoryTypeGroup() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + lb.inventoryTypeGroup(65); + }); + assertMismatch( + actual, unit, startsWith("InventoryTypeGroup was but expected ") + ); + } + + @Test + void testSpeciesDifferent() { + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + + if (expectedSpec.getGenus().equals("MB")) { + sb.decayGroup(3); + } + + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + assertMismatch( + actual, unit, + startsWith("mismatch in species group \"MB\": DecayGroup was <3> but expected <13>") + ); + } + + @ParameterizedTest + @EnumSource(UtilizationClassVariable.class) + void testUtilizationDifferent(UtilizationClassVariable ucv) throws Exception { + + var actual = VdypLayer.build(lb -> { + lb.copy(expected); + lb.copySpecies(expected, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + + // Make a change to one entry (SMALL) in the utilization vector for the specified field + + ucv.get(actual).scalarInPlace(UtilizationClass.SMALL, x -> x + 1); + + String name = ucv.getShortName(); + String type = "a utilization vector"; + String floatPattern = "(\\d+\\.\\d+)"; + String expectedVectorPattern = "\\[Small: " + floatPattern + ", All: " + floatPattern + ", 7.5cm: " + + floatPattern + ", 12.5cm: " + floatPattern + ", 17.5cm: " + floatPattern + ", 22.5cm: " + + floatPattern + "\\]"; + + String actualVectorPattern = "\\[Small: \\[\\[" + floatPattern + + "\\]\\], All: \\2, 7.5cm: \\3, 12.5cm: \\4, 17.5cm: \\5, 22.5cm: \\6\\]"; + + if (UtilizationClassVariable.LOREY_HEIGHT == ucv) { + type = "a lorey height vector"; + expectedVectorPattern = "\\[Small: " + floatPattern + ", All: " + floatPattern + "\\]"; + actualVectorPattern = "\\[Small: \\[\\[" + floatPattern + "\\]\\], All: \\2\\]"; + } + + assertMismatch( + actual, unit, + matchesRegex( + "^" + name + " expected " + type + " \"" + expectedVectorPattern + "\" but was " + + actualVectorPattern + "$" + ) + ); + } + } + @Nested class testVdypSpecies { VdypSpecies expected; diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java index 1086a60c7..a24a322f2 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java @@ -1,7 +1,6 @@ package ca.bc.gov.nrs.vdyp.forward; import java.util.Arrays; -import java.util.Map; import java.util.Optional; import java.util.function.Predicate; @@ -10,10 +9,7 @@ import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; import ca.bc.gov.nrs.vdyp.forward.model.ControlVariable; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; -import ca.bc.gov.nrs.vdyp.model.MatrixMap2; -import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypEntity; @@ -21,7 +17,6 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; -import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; class ForwardLayerProcessingState extends LayerProcessingState { From 6f45153b32307fcac2cce07d3bfdd5471721966b Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 18 Nov 2024 20:03:29 -0800 Subject: [PATCH 35/45] Unit tests for polygon deep equals --- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 2 +- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 205 ++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 8be2c3a05..ed7098b9c 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -804,7 +804,7 @@ static private boolean matchValueShowExpected( return match; } - Matcher deepEquals(final VdypPolygon expected) { + public static Matcher deepEquals(final VdypPolygon expected) { return new TypeSafeDiagnosingMatcher() { @Override diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 270a7d48e..509111095 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -33,6 +33,7 @@ import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSite; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; @@ -69,6 +70,210 @@ UtilizationVector mockHeightVector() { @Nested class deepEquals { + @Nested + class testVdypPolygon { + VdypPolygon expected; + Matcher unit; + Map controlMap; + + @BeforeEach + void setup() { + Random rand = new Random(42); + controlMap = TestUtils.loadControlMap(); + + expected = VdypPolygon.build(pb -> { + + pb.polygonIdentifier("Test", 2024); + pb.percentAvailable(90f); + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.addLayer(lb -> { + + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + }); + + unit = VdypMatchers.deepEquals(expected); + + } + + @Test + void testPass() { + var actual = VdypPolygon.build(pb -> { + pb.copy(expected); + pb.copyLayers(expected, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + + assertMatch(actual, unit); + } + + // Changing the key properties also causes mismatches on the children that share those key properties so use + // startsWith + + @Test + void testPolyIdDifferent() { + var actual = VdypPolygon.build(pb -> { + pb.copy(expected); + + pb.polygonIdentifier("Different", 2025); + + pb.copyLayers(expected, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + + assertMismatch( + actual, unit, + startsWith( + "PolygonIdentifier was but expected " + ) + ); + } + + @Test + void testBecDifferent() { + var actual = VdypPolygon.build(pb -> { + pb.copy(expected); + + pb.biogeoclimaticZone(Utils.getBec("IDF", controlMap)); + + pb.copyLayers(expected, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + + assertMismatch( + actual, unit, + startsWith( + "BiogeoclimaticZone was but expected " + ) + ); + } + + @Test + void testFizDifferent() { + var actual = VdypPolygon.build(pb -> { + pb.copy(expected); + + pb.forestInventoryZone("A"); + + pb.copyLayers(expected, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + + assertMismatch(actual, unit, startsWith("ForestInventoryZone was \"A\" but expected \"Z\"")); + } + + @Test + void testLayerDifferent() { + var actual = VdypPolygon.build(pb -> { + pb.copy(expected); + + pb.copyLayers(expected, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.layerType(LayerType.VETERAN); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + + assertMismatch(actual, unit, startsWith("Layers was <[VETERAN]> but expected <[PRIMARY]>")); + } + + } @Nested class testVdypLayer { From 7f2e97c2355e68965f28b84be25fb309931b9d50 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 19 Nov 2024 12:36:29 -0800 Subject: [PATCH 36/45] use UtilizationClassVariable accessor enum in place of reflection --- .../application/VdypStartApplication.java | 245 +++++++----------- .../nrs/vdyp/common/ComputationMethods.java | 73 +++--- .../ca/bc/gov/nrs/vdyp/model/VdypSpecies.java | 1 - .../variables/UtilizationClassVariable.java | 81 ++++-- .../LayerProcessingState.java | 98 ++++--- .../test/TestStartApplication.java | 1 + .../java/ca/bc/gov/nrs/vdyp/fip/FipStart.java | 19 +- .../forward/ForwardLayerProcessingState.java | 20 +- .../Grow11UpdateCompatibilityVariables.java | 10 +- 9 files changed, 268 insertions(+), 280 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java index 51977e07c..0b5c19e85 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java @@ -35,6 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.common.ComputationMethods; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.EstimationMethods; import ca.bc.gov.nrs.vdyp.common.ReconcilationMethods; @@ -131,41 +132,6 @@ protected static void doMain(VdypStartApplication app, final String. } } - /** - * Accessor methods for utilization vectors, except for Lorey Height, on Layer and Species objects. - */ - protected static final Collection UTILIZATION_VECTOR_ACCESSORS; - - /** - * Accessor methods for utilization vectors, except for Lorey Height and Quadratic Mean Diameter, on Layer and - * Species objects. These are properties where the values for the layer are the sum of those for its species. - */ - static final Collection SUMMABLE_UTILIZATION_VECTOR_ACCESSORS; - - /** - * Accessor methods for utilization vectors, except for Lorey Height,and Volume on Layer and Species objects. - */ - protected static final Collection NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS; - - static { - try { - var bean = Introspector.getBeanInfo(VdypUtilizationHolder.class); - UTILIZATION_VECTOR_ACCESSORS = Arrays.stream(bean.getPropertyDescriptors()) // - .filter(p -> p.getName().endsWith("ByUtilization")) // - .filter(p -> !p.getName().startsWith("loreyHeight")) // - .filter(p -> p.getPropertyType() == UtilizationVector.class) // - .toList(); - } catch (IntrospectionException e) { - throw new IllegalStateException(e); - } - - SUMMABLE_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().startsWith("quadraticMeanDiameter")).toList(); - - NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().contains("Volume")).toList(); - } - protected VdypOutputWriter vriWriter; protected Map controlMap = new HashMap<>(); @@ -222,7 +188,6 @@ public void init(FileSystemFileResolver resolver, String... controlFilePaths) * @throws IOException */ public void init(FileSystemFileResolver resolver, Map controlMap) throws IOException { - setControlMap(controlMap); closeVriWriter(); vriWriter = createWriter(resolver, controlMap); @@ -1359,7 +1324,7 @@ public void computeUtilizationComponentsPrimary( protected void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) { // Layer utilization vectors other than quadratic mean diameter are the pairwise // sums of those of their species - sumSpeciesUtilizationVectorsToLayer(vdypLayer); + ComputationMethods.sumSpeciesUtilizationVectorsToLayer(vdypLayer); { var hlVector = Utils.heightVector(); @@ -1391,34 +1356,6 @@ protected void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) } - // TODO De-reflectify this when we want to make it work in GralVM - void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - var utilVector = Utils.utilizationVector(); - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - var speciesVector = (Coefficients) accessors.getReadMethod().invoke(vdypSpecies); - utilVector.pairwiseInPlace(speciesVector, (x, y) -> x + y); - } - accessors.getWriteMethod().invoke(vdypLayer, utilVector); - } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - - // TODO De-reflectify this when we want to make it work in GralVM - protected void scaleAllSummableUtilization(VdypUtilizationHolder holder, float factor) - throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - ((Coefficients) accessors.getReadMethod().invoke(holder)).scalarInPlace(x -> x * factor); - } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); - } - } - // YUCV protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinition bec) throws ProcessingException { @@ -1430,114 +1367,110 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin var volumeAdjustMap = Utils.>expectParsedControl( controlMap, ControlKey.VETERAN_LAYER_VOLUME_ADJUST, java.util.Map.class ); - try { - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - - var treesPerHectareUtil = Utils.utilizationVector(); - var quadMeanDiameterUtil = Utils.utilizationVector(); - var baseAreaUtil = Utils.utilizationVector(); - var wholeStemVolumeUtil = Utils.utilizationVector(); - - var closeUtilizationVolumeUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); - var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); - - var hlSp = vdypSpecies.getLoreyHeightByUtilization().getAll(); - { - var baSp = vdypSpecies.getBaseAreaByUtilization().getLarge(); - var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getLarge(); - var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getLarge(); - - treesPerHectareUtil.setAll(tphSp); - quadMeanDiameterUtil.setAll(dqSp); - baseAreaUtil.setAll(baSp); - wholeStemVolumeUtil.setAll(0f); - - treesPerHectareUtil.setLarge(tphSp); - quadMeanDiameterUtil.setLarge(dqSp); - baseAreaUtil.setLarge(baSp); - wholeStemVolumeUtil.setLarge(0f); - } - // AADJUSTV - var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); + for (var vdypSpecies : vdypLayer.getSpecies().values()) { - var utilizationClass = UtilizationClass.OVER225; // IUC_VET + var treesPerHectareUtil = Utils.utilizationVector(); + var quadMeanDiameterUtil = Utils.utilizationVector(); + var baseAreaUtil = Utils.utilizationVector(); + var wholeStemVolumeUtil = Utils.utilizationVector(); - // ADJ - var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); + var closeUtilizationVolumeUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); + + var hlSp = vdypSpecies.getLoreyHeightByUtilization().getAll(); + { + var baSp = vdypSpecies.getBaseAreaByUtilization().getLarge(); + var tphSp = vdypSpecies.getTreesPerHectareByUtilization().getLarge(); + var dqSp = vdypSpecies.getQuadraticMeanDiameterByUtilization().getLarge(); + + treesPerHectareUtil.setAll(tphSp); + quadMeanDiameterUtil.setAll(dqSp); + baseAreaUtil.setAll(baSp); + wholeStemVolumeUtil.setAll(0f); + + treesPerHectareUtil.setLarge(tphSp); + quadMeanDiameterUtil.setLarge(dqSp); + baseAreaUtil.setLarge(baSp); + wholeStemVolumeUtil.setLarge(0f); + } + // AADJUSTV + var volumeAdjustCoe = volumeAdjustMap.get(vdypSpecies.getGenus()); - // EMP091 - estimationMethods.estimateWholeStemVolume( - utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, - quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil - ); + var utilizationClass = UtilizationClass.OVER225; // IUC_VET - adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); - // EMP092 - estimationMethods.estimateCloseUtilizationVolume( - utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, - wholeStemVolumeUtil, closeUtilizationVolumeUtil - ); + // ADJ + var adjust = new Coefficients(new float[] { 0f, 0f, 0f, 0f }, 1); - adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); - // EMP093 - estimationMethods.estimateNetDecayVolume( - vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, vdypSpecies.getDecayGroup(), - vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeUtilizationVolumeUtil, - closeUtilizationNetOfDecayUtil - ); + // EMP091 + estimationMethods.estimateWholeStemVolume( + utilizationClass, volumeAdjustCoe.getCoe(1), vdypSpecies.getVolumeGroup(), hlSp, + quadMeanDiameterUtil, baseAreaUtil, wholeStemVolumeUtil + ); - adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); - // EMP094 - estimationMethods.estimateNetDecayAndWasteVolume( - bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, quadMeanDiameterUtil, - closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, - closeUtilizationNetOfDecayAndWasteUtil - ); + adjust.setCoe(4, volumeAdjustCoe.getCoe(2)); + // EMP092 + estimationMethods.estimateCloseUtilizationVolume( + utilizationClass, adjust, vdypSpecies.getVolumeGroup(), hlSp, quadMeanDiameterUtil, + wholeStemVolumeUtil, closeUtilizationVolumeUtil + ); - if (getId().isStart()) { - // EMP095 - estimationMethods.estimateNetDecayWasteAndBreakageVolume( - utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, - closeUtilizationVolumeUtil, closeUtilizationNetOfDecayAndWasteUtil, - closeUtilizationNetOfDecayWasteAndBreakageUtil - ); - } + adjust.setCoe(4, volumeAdjustCoe.getCoe(3)); + // EMP093 + estimationMethods.estimateNetDecayVolume( + vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, vdypSpecies.getDecayGroup(), + vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeUtilizationVolumeUtil, + closeUtilizationNetOfDecayUtil + ); - vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); - vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); - vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); - vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - closeUtilizationNetOfDecayAndWasteUtil - ); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); + // EMP094 + estimationMethods.estimateNetDecayAndWasteVolume( + bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, quadMeanDiameterUtil, + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, + closeUtilizationNetOfDecayAndWasteUtil + ); + + if (getId().isStart()) { + // EMP095 + estimationMethods.estimateNetDecayWasteAndBreakageVolume( + utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayAndWasteUtil, closeUtilizationNetOfDecayWasteAndBreakageUtil ); + } - for (var accessors : UTILIZATION_VECTOR_ACCESSORS) { - UtilizationVector utilVector = (UtilizationVector) accessors.getReadMethod().invoke(vdypSpecies); - - // Set all components other than 4 to 0.0 - for (var uc : UtilizationClass.ALL_BUT_LARGEST) { - utilVector.set(uc, 0f); - } + vdypSpecies.setBaseAreaByUtilization(baseAreaUtil); + vdypSpecies.setTreesPerHectareByUtilization(treesPerHectareUtil); + vdypSpecies.setQuadraticMeanDiameterByUtilization(quadMeanDiameterUtil); + vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); + vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + closeUtilizationNetOfDecayAndWasteUtil + ); + vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + closeUtilizationNetOfDecayWasteAndBreakageUtil + ); - // Set component 0 to equal component 4. - utilVector.setAll(utilVector.getLarge()); + for (var accessors : ComputationMethods.UTILIZATION_VECTOR_ACCESSORS) { + UtilizationVector utilVector = (UtilizationVector) accessors.get(vdypSpecies); - accessors.getWriteMethod().invoke(vdypSpecies, utilVector); + // Set all components other than 4 to 0.0 + for (var uc : UtilizationClass.ALL_BUT_LARGEST) { + utilVector.set(uc, 0f); } - } - computeLayerUtilizationComponentsFromSpecies(vdypLayer); + // Set component 0 to equal component 4. + utilVector.setAll(utilVector.getLarge()); - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); + accessors.set(vdypSpecies, utilVector); + } } + + computeLayerUtilizationComponentsFromSpecies(vdypLayer); + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java index 4c127b9ad..60ebd273f 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java @@ -1,9 +1,5 @@ package ca.bc.gov.nrs.vdyp.common; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collection; import java.util.stream.Collectors; @@ -20,7 +16,6 @@ import ca.bc.gov.nrs.vdyp.model.Coefficients; import ca.bc.gov.nrs.vdyp.model.CompatibilityVariableMode; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; @@ -30,41 +25,34 @@ public class ComputationMethods { - public static final Logger log = LoggerFactory.getLogger(VdypStartApplication.class); + public static final Logger log = LoggerFactory.getLogger(ComputationMethods.class); /** * Accessor methods for utilization vectors, except for Lorey Height, on Layer and Species objects. */ - protected static final Collection UTILIZATION_VECTOR_ACCESSORS; + public static final Collection UTILIZATION_VECTOR_ACCESSORS; /** * Accessor methods for utilization vectors, except for Lorey Height and Quadratic Mean Diameter, on Layer and * Species objects. These are properties where the values for the layer are the sum of those for its species. */ - public static final Collection SUMMABLE_UTILIZATION_VECTOR_ACCESSORS; + public static final Collection SUMMABLE_UTILIZATION_VECTOR_ACCESSORS; /** * Accessor methods for utilization vectors, except for Lorey Height,and Volume on Layer and Species objects. */ - protected static final Collection NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS; + public static final Collection NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS; static { - try { - var bean = Introspector.getBeanInfo(VdypUtilizationHolder.class); - UTILIZATION_VECTOR_ACCESSORS = Arrays.stream(bean.getPropertyDescriptors()) // - .filter(p -> p.getName().endsWith("ByUtilization")) // - .filter(p -> !p.getName().startsWith("loreyHeight")) // - .filter(p -> p.getPropertyType() == UtilizationVector.class) // - .toList(); - } catch (IntrospectionException e) { - throw new IllegalStateException(e); - } + UTILIZATION_VECTOR_ACCESSORS = Arrays.stream(UtilizationClassVariable.values()) // + .filter(p -> p != UtilizationClassVariable.LOREY_HEIGHT) // + .toList(); SUMMABLE_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().startsWith("quadraticMeanDiameter")).toList(); + .filter(x -> x != UtilizationClassVariable.QUAD_MEAN_DIAMETER).toList(); NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS = UTILIZATION_VECTOR_ACCESSORS.stream() - .filter(x -> !x.getName().contains("Volume")).toList(); + .filter(x -> !UtilizationClassVariable.VOLUME_VARIABLES.contains(x)).toList(); } private final EstimationMethods estimationMethods; @@ -391,31 +379,32 @@ protected static void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdy } } - // TODO De-reflectify this when we want to make it work in GraalVM - private static void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - var utilVector = Utils.utilizationVector(); - for (var vdypSpecies : vdypLayer.getSpecies().values()) { - var speciesVector = (Coefficients) accessors.getReadMethod().invoke(vdypSpecies); - utilVector.pairwiseInPlace(speciesVector, (x, y) -> x + y); - } - accessors.getWriteMethod().invoke(vdypLayer, utilVector); + /** + * For those utilization fields that can be summed, add together the vectors for each species in the given layer and + * store the result on the layer. + * + * @param vdypLayer + */ + public static void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) { + for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { + var utilVector = Utils.utilizationVector(); + for (var vdypSpecies : vdypLayer.getSpecies().values()) { + var speciesVector = (Coefficients) accessors.get(vdypSpecies); + utilVector.pairwiseInPlace(speciesVector, (x, y) -> x + y); } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); + accessors.set(vdypLayer, utilVector); } } - // TODO De-reflectify this when we want to make it work in GralVM - protected static void scaleAllSummableUtilization(VdypUtilizationHolder holder, float factor) - throws IllegalStateException { - try { - for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - ((Coefficients) accessors.getReadMethod().invoke(holder)).scalarInPlace(x -> x * factor); - } - } catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException(ex); + /** + * For those utilization fields that can be summed, scale them by the given value. + * + * @param holder + * @param factor + */ + public static void scaleAllSummableUtilization(VdypUtilizationHolder holder, float factor) { + for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { + ((Coefficients) accessors.get(holder)).scalarInPlace(x -> x * factor); } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java index 54b2914ca..485cc2d74 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypSpecies.java @@ -10,7 +10,6 @@ import ca.bc.gov.nrs.vdyp.application.InitializationIncompleteException; import ca.bc.gov.nrs.vdyp.common.Utils; -import ca.bc.gov.nrs.vdyp.model.BaseVdypSpecies.Builder; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class VdypSpecies extends BaseVdypSpecies implements VdypUtilizationHolder { diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java index 3597ebe4b..fa4db8255 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java @@ -2,6 +2,7 @@ import java.util.EnumSet; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; @@ -11,45 +12,90 @@ /** * Represents fields that have a UtilizationVector as a type */ -public enum UtilizationClassVariable implements Property { - LOREY_HEIGHT("LoreyHeight", VdypUtilizationHolder::getLoreyHeightByUtilization) { +public enum UtilizationClassVariable implements Variable { + LOREY_HEIGHT( + "LoreyHeight", + VdypUtilizationHolder::getLoreyHeightByUtilization, + VdypUtilizationHolder::setLoreyHeightByUtilization + ) { // Lorey Height only has 2 classes instead of the usual 6 @Override public Set getClasses() { return HEIGHT_CLASSES; } }, - BASAL_AREA("BaseArea", VdypUtilizationHolder::getBaseAreaByUtilization), - QUAD_MEAN_DIAMETER("QuadraticMeanDiameter", VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization), - TREES_PER_HECTARE("TreesPerHectare", VdypUtilizationHolder::getTreesPerHectareByUtilization), + BASAL_AREA( + "BaseArea", + VdypUtilizationHolder::getBaseAreaByUtilization, + VdypUtilizationHolder::setBaseAreaByUtilization + ), + QUAD_MEAN_DIAMETER( + "QuadraticMeanDiameter", + VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization, + VdypUtilizationHolder::setQuadraticMeanDiameterByUtilization + ), + TREES_PER_HECTARE( + "TreesPerHectare", + VdypUtilizationHolder::getTreesPerHectareByUtilization, + VdypUtilizationHolder::setTreesPerHectareByUtilization + ), - WHOLE_STEM_VOL("WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization), - CLOSE_UTIL_VOL("CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization), + WHOLE_STEM_VOL( + "WholeStemVolume", + VdypUtilizationHolder::getWholeStemVolumeByUtilization, + VdypUtilizationHolder::setWholeStemVolumeByUtilization + ), + CLOSE_UTIL_VOL( + "CloseUtilizationVolume", + VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization, + VdypUtilizationHolder::setCloseUtilizationVolumeByUtilization + ), CLOSE_UTIL_VOL_LESS_DECAY( - "CloseUtilizationVolumeNetOfDecay", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization + "CloseUtilizationVolumeNetOfDecay", + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization, + VdypUtilizationHolder::setCloseUtilizationVolumeNetOfDecayByUtilization ), CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE( "CloseUtilizationVolumeNetOfDecayAndWaste", - VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization, + VdypUtilizationHolder::setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization ), CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE( "CloseUtilizationVolumeNetOfDecayWasteAndBreakage", - VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization + VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization, + VdypUtilizationHolder::setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization ); - static private final Set STANDARD_CLASSES = EnumSet.allOf(UtilizationClass.class); - static private final Set HEIGHT_CLASSES = EnumSet - .of(UtilizationClass.SMALL, UtilizationClass.ALL); + private static final Set STANDARD_CLASSES = EnumSet.allOf(UtilizationClass.class); + private static final Set HEIGHT_CLASSES = EnumSet + .of( + UtilizationClass.SMALL, + UtilizationClass.ALL + ); + + public static final Set VOLUME_VARIABLES = EnumSet + .of( + WHOLE_STEM_VOL, + CLOSE_UTIL_VOL, + CLOSE_UTIL_VOL_LESS_DECAY, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE + ); private final String shortName; private final String longName; private final Function getter; + private final BiConsumer setter; - private UtilizationClassVariable(String shortName, Function accessor) { + private UtilizationClassVariable( + String shortName, Function getter, + BiConsumer setter + ) { this.shortName = shortName; // Pre-computing it at init instead of using the default to improve performance this.longName = shortName + "ByUtilization"; - this.getter = accessor; + this.getter = getter; + this.setter = setter; } @Override @@ -79,4 +125,9 @@ public String getName() { public Set getClasses() { return STANDARD_CLASSES; } + + @Override + public void set(VdypUtilizationHolder parent, UtilizationVector value) { + setter.accept(parent, value); + } } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index b58d661b3..f000bc4a1 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -1,7 +1,11 @@ package ca.bc.gov.nrs.vdyp.processing_state; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Predicate; +import java.util.stream.IntStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +17,7 @@ import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; @@ -44,12 +49,7 @@ public abstract class LayerProcessingState[] cvVolume; - private MatrixMap2[] cvBasalArea; - private MatrixMap2[] cvQuadraticMeanDiameter; - private Map[] cvPrimaryLayerSmall; + private Optional> compatibilityVariables = Optional.empty(); protected LayerProcessingState(ProcessingState ps, VdypPolygon polygon, LayerType subjectLayerType) { @@ -101,66 +101,84 @@ public void setCompatibilityVariableDetails( MatrixMap2[] cvQuadraticMeanDiameter, Map[] cvPrimaryLayerSmall ) { - if (areCompatibilityVariablesSet) { + if (compatibilityVariables.isPresent()) { throw new IllegalStateException(COMPATIBILITY_VARIABLES_SET_CAN_BE_SET_ONCE_ONLY); } + int n = cvVolume.length; - this.cvVolume = cvVolume; - this.cvBasalArea = cvBasalArea; - this.cvQuadraticMeanDiameter = cvQuadraticMeanDiameter; - this.cvPrimaryLayerSmall = cvPrimaryLayerSmall; + compatibilityVariables = Optional.of( + IntStream.range(0, n) + .mapToObj(i -> { + if (cvVolume[i] == null) + return null; + return VdypCompatibilityVariables.build(cvb -> { - areCompatibilityVariablesSet = true; + cvb.cvVolume(cvVolume[i]); + cvb.cvBasalArea(cvBasalArea[i]); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter[i]); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall[i]); + }); + }).toList() + ); } public float getCVVolume( int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable, LayerType layerType ) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_VOLUMES); - } - - return cvVolume[speciesIndex].get(uc, volumeVariable, layerType); + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvVolume(uc, volumeVariable, layerType); } public float getCVBasalArea(int speciesIndex, UtilizationClass uc, LayerType layerType) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvBasalArea[speciesIndex].get(uc, layerType); + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvBasalArea(uc, layerType); } public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc, LayerType layerType) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvQuadraticMeanDiameter[speciesIndex].get(uc, layerType); + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvQuadraticMeanDiameter(uc, layerType); } public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { - if (!areCompatibilityVariablesSet) { - throw new IllegalStateException(UNSET_CV_BASAL_AREAS); - } - - return cvPrimaryLayerSmall[speciesIndex].get(variable); + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvPrimaryLayerSmall(variable); } - public MatrixMap3[] getCvVolume() { - return cvVolume; + public MatrixMap3 getCvVolume(int speciesIndex) { + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvVolume(); } - public MatrixMap2[] getCvBasalArea() { - return cvBasalArea; + public MatrixMap2 getCvBasalArea(int speciesIndex) { + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvBasalArea(); } - public MatrixMap2[] getCvQuadraticMeanDiameter() { - return cvQuadraticMeanDiameter; + public MatrixMap2 getCvQuadraticMeanDiameter(int speciesIndex) { + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvQuadraticMeanDiameter(); } - public Map[] getCvPrimaryLayerSmall() { - return cvPrimaryLayerSmall; + public Map getCvPrimaryLayerSmall(int speciesIndex) { + return compatibilityVariables + .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) + .get(speciesIndex) + .getCvPrimaryLayerSmall(); + } } \ No newline at end of file diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestStartApplication.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestStartApplication.java index 49077c062..c01970fcf 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestStartApplication.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/application/test/TestStartApplication.java @@ -11,6 +11,7 @@ import ca.bc.gov.nrs.vdyp.application.VdypStartApplication; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; 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.ControlMapValueReplacer; diff --git a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java index ebcb840bd..67de00fc4 100644 --- a/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java +++ b/lib/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java @@ -9,7 +9,6 @@ import static java.lang.Math.min; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; @@ -45,6 +44,7 @@ import ca.bc.gov.nrs.vdyp.application.StandProcessingException; import ca.bc.gov.nrs.vdyp.application.VdypApplicationIdentifier; import ca.bc.gov.nrs.vdyp.application.VdypStartApplication; +import ca.bc.gov.nrs.vdyp.common.ComputationMethods; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; @@ -250,8 +250,8 @@ void adjustForStocking(VdypLayer vdypLayer, FipLayerPrimary fipLayerPrimary, Bec float factor = factorEntry.get().getFactor(); - scaleAllSummableUtilization(vdypLayer, factor); - vdypLayer.getSpecies().values().forEach(spec -> scaleAllSummableUtilization(spec, factor)); + ComputationMethods.scaleAllSummableUtilization(vdypLayer, factor); + vdypLayer.getSpecies().values().forEach(spec -> ComputationMethods.scaleAllSummableUtilization(spec, factor)); log.atInfo().addArgument(fipLayerPrimary.getStockingClass()).addArgument(factor).setMessage( "Foregoing Primary Layer has stocking class {} Yield values will be multiplied by {} before being written to output file." @@ -412,15 +412,12 @@ void findRootsForDiameterAndBaseArea(VdypLayer result, FipLayerPrimary fipLayer, if (result.getSpecies().size() == 1) { var spec = result.getSpecies().values().iterator().next(); - for (var accessors : NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS) { + for (var accessors : ComputationMethods.NON_VOLUME_UTILIZATION_VECTOR_ACCESSORS) { + + UtilizationVector specVector = (UtilizationVector) accessors.get(spec); + UtilizationVector layerVector = (UtilizationVector) accessors.get(result); + specVector.setAll(layerVector.getAll()); - try { - UtilizationVector specVector = (UtilizationVector) accessors.getReadMethod().invoke(spec); - UtilizationVector layerVector = (UtilizationVector) accessors.getReadMethod().invoke(result); - specVector.setAll(layerVector.getAll()); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new IllegalStateException(e); - } } result.getLoreyHeightByUtilization().setAll(spec.getLoreyHeightByUtilization().getAll()); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java index a24a322f2..c399029fd 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java @@ -322,28 +322,28 @@ public void updateCompatibilityVariablesAfterGrowth() { for (int i : getIndices()) { for (UtilizationClassVariable sucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - getCvPrimaryLayerSmall()[i].put( + getCvPrimaryLayerSmall(i).put( sucv, - getCvPrimaryLayerSmall()[i].get(sucv) + getCvPrimaryLayerSmall(i).get(sucv) * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) ); } for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - getCvBasalArea()[i].put( + getCvBasalArea(i).put( uc, LayerType.PRIMARY, - getCvBasalArea()[i].get(uc, LayerType.PRIMARY) + getCvBasalArea(i).get(uc, LayerType.PRIMARY) * compVarAdjustments.getValue(uc, UtilizationClassVariable.BASAL_AREA) ); - getCvQuadraticMeanDiameter()[i].put( + getCvQuadraticMeanDiameter(i).put( uc, LayerType.PRIMARY, - getCvQuadraticMeanDiameter()[i].get(uc, LayerType.PRIMARY) + getCvQuadraticMeanDiameter(i).get(uc, LayerType.PRIMARY) * compVarAdjustments.getValue(uc, UtilizationClassVariable.QUAD_MEAN_DIAMETER) ); for (UtilizationClassVariable vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { - getCvVolume()[i].put( + getCvVolume(i).put( uc, vv, LayerType.PRIMARY, - getCvVolume()[i].get(uc, vv, LayerType.PRIMARY) * compVarAdjustments.getVolumeValue(uc, vv) + getCvVolume(i).get(uc, vv, LayerType.PRIMARY) * compVarAdjustments.getVolumeValue(uc, vv) ); } } @@ -361,8 +361,8 @@ protected VdypLayer updateLayerFromBank() { VdypSpecies species = updatedLayer.getSpeciesBySp0(getBank().speciesNames[i]); species.setCompatibilityVariables( - getCvVolume()[i], getCvBasalArea()[i], getCvQuadraticMeanDiameter()[i], - getCvPrimaryLayerSmall()[i] + getCvVolume(i), getCvBasalArea(i), getCvQuadraticMeanDiameter(i), + getCvPrimaryLayerSmall(i) ); } } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index d18b70fdc..e4ccd77e9 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -61,7 +61,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); assertThat( // VDYP7 reports BASAL_AREA = -2.13947629e-07, all others 0.0 - lps.getCvPrimaryLayerSmall()[1], + lps.getCvPrimaryLayerSmall(1), Matchers.allOf( Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, -2.1394816e-07f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), @@ -72,7 +72,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { assertThat( // VDYP7 reports BASAL_AREA = -4.49605286e-05, QUAD_MEAN_DIAMETER = 0.00236749649 // LOREY_HEIGHT = 1.19209221e-06, WHOLE_STEM_VOLUME = 0.00102931913 - lps.getCvPrimaryLayerSmall()[2], + lps.getCvPrimaryLayerSmall(2), Matchers.allOf( Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, -4.406223e-5f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0023196794f), @@ -83,7 +83,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { assertThat( // VDYP7 reports BASAL_AREA = 4.94660344e-6, QUAD_MEAN_DIAMETER = 0.0 // LOREY_HEIGHT = -1.55569342e-5, WHOLE_STEM_VOLUME = 0.0 - lps.getCvPrimaryLayerSmall()[3], + lps.getCvPrimaryLayerSmall(3), Matchers.allOf( Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 4.8476713e-6f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), @@ -93,7 +93,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { ); assertThat( // VDYP7 reports 0.0 for all - lps.getCvPrimaryLayerSmall()[4], + lps.getCvPrimaryLayerSmall(4), Matchers.allOf( Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 0.0f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), @@ -103,7 +103,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { ); assertThat( // VDYP7 reports BASAL_AREA = 3.42086423e-06, LOREY_HEIGHT = -5.7758567e-5, 0.0 for all others - lps.getCvPrimaryLayerSmall()[5], + lps.getCvPrimaryLayerSmall(5), Matchers.allOf( Matchers.hasEntry(UtilizationClassVariable.BASAL_AREA, 3.352447e-6f), Matchers.hasEntry(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 0.0f), From 8703160bfb07cfa294ec57e87c63c884740c53c7 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 19 Nov 2024 13:58:32 -0800 Subject: [PATCH 37/45] Fix error druing CV update --- .../application/VdypStartApplication.java | 8 +-- .../nrs/vdyp/common/ComputationMethods.java | 4 +- .../variables/UtilizationClassVariable.java | 39 ++++--------- .../LayerProcessingState.java | 57 +++++++------------ .../forward/ForwardLayerProcessingState.java | 6 +- .../vdyp/forward/ForwardProcessingState.java | 3 +- 6 files changed, 39 insertions(+), 78 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java index 0b5c19e85..ffccffe2a 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/VdypStartApplication.java @@ -1428,8 +1428,7 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin // EMP094 estimationMethods.estimateNetDecayAndWasteVolume( bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, quadMeanDiameterUtil, - closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, - closeUtilizationNetOfDecayAndWasteUtil + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil ); if (getId().isStart()) { @@ -1447,9 +1446,8 @@ protected void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefin vdypSpecies.setWholeStemVolumeByUtilization(wholeStemVolumeUtil); vdypSpecies.setCloseUtilizationVolumeByUtilization(closeUtilizationVolumeUtil); vdypSpecies.setCloseUtilizationVolumeNetOfDecayByUtilization(closeUtilizationNetOfDecayUtil); - vdypSpecies.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - closeUtilizationNetOfDecayAndWasteUtil - ); + vdypSpecies + .setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(closeUtilizationNetOfDecayAndWasteUtil); vdypSpecies.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( closeUtilizationNetOfDecayWasteAndBreakageUtil ); diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java index 60ebd273f..1b59f65ce 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ComputationMethods.java @@ -382,7 +382,7 @@ protected static void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdy /** * For those utilization fields that can be summed, add together the vectors for each species in the given layer and * store the result on the layer. - * + * * @param vdypLayer */ public static void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) { @@ -398,7 +398,7 @@ public static void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) { /** * For those utilization fields that can be summed, scale them by the given value. - * + * * @param holder * @param factor */ diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java index fa4db8255..80314dcae 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/variables/UtilizationClassVariable.java @@ -14,8 +14,7 @@ */ public enum UtilizationClassVariable implements Variable { LOREY_HEIGHT( - "LoreyHeight", - VdypUtilizationHolder::getLoreyHeightByUtilization, + "LoreyHeight", VdypUtilizationHolder::getLoreyHeightByUtilization, VdypUtilizationHolder::setLoreyHeightByUtilization ) { // Lorey Height only has 2 classes instead of the usual 6 @@ -25,34 +24,27 @@ public Set getClasses() { } }, BASAL_AREA( - "BaseArea", - VdypUtilizationHolder::getBaseAreaByUtilization, - VdypUtilizationHolder::setBaseAreaByUtilization + "BaseArea", VdypUtilizationHolder::getBaseAreaByUtilization, VdypUtilizationHolder::setBaseAreaByUtilization ), QUAD_MEAN_DIAMETER( - "QuadraticMeanDiameter", - VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization, + "QuadraticMeanDiameter", VdypUtilizationHolder::getQuadraticMeanDiameterByUtilization, VdypUtilizationHolder::setQuadraticMeanDiameterByUtilization ), TREES_PER_HECTARE( - "TreesPerHectare", - VdypUtilizationHolder::getTreesPerHectareByUtilization, + "TreesPerHectare", VdypUtilizationHolder::getTreesPerHectareByUtilization, VdypUtilizationHolder::setTreesPerHectareByUtilization ), WHOLE_STEM_VOL( - "WholeStemVolume", - VdypUtilizationHolder::getWholeStemVolumeByUtilization, + "WholeStemVolume", VdypUtilizationHolder::getWholeStemVolumeByUtilization, VdypUtilizationHolder::setWholeStemVolumeByUtilization ), CLOSE_UTIL_VOL( - "CloseUtilizationVolume", - VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization, + "CloseUtilizationVolume", VdypUtilizationHolder::getCloseUtilizationVolumeByUtilization, VdypUtilizationHolder::setCloseUtilizationVolumeByUtilization ), CLOSE_UTIL_VOL_LESS_DECAY( - "CloseUtilizationVolumeNetOfDecay", - VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization, + "CloseUtilizationVolumeNetOfDecay", VdypUtilizationHolder::getCloseUtilizationVolumeNetOfDecayByUtilization, VdypUtilizationHolder::setCloseUtilizationVolumeNetOfDecayByUtilization ), CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE( @@ -68,19 +60,12 @@ public Set getClasses() { private static final Set STANDARD_CLASSES = EnumSet.allOf(UtilizationClass.class); private static final Set HEIGHT_CLASSES = EnumSet - .of( - UtilizationClass.SMALL, - UtilizationClass.ALL - ); + .of(UtilizationClass.SMALL, UtilizationClass.ALL); - public static final Set VOLUME_VARIABLES = EnumSet - .of( - WHOLE_STEM_VOL, - CLOSE_UTIL_VOL, - CLOSE_UTIL_VOL_LESS_DECAY, - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, - CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE - ); + public static final Set VOLUME_VARIABLES = EnumSet.of( + WHOLE_STEM_VOL, CLOSE_UTIL_VOL, CLOSE_UTIL_VOL_LESS_DECAY, CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE_LESS_BREAKAGE + ); private final String shortName; private final String longName; diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index f000bc4a1..4e9cda43b 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -106,77 +106,58 @@ public void setCompatibilityVariableDetails( } int n = cvVolume.length; - compatibilityVariables = Optional.of( - IntStream.range(0, n) - .mapToObj(i -> { - if (cvVolume[i] == null) - return null; - return VdypCompatibilityVariables.build(cvb -> { - - cvb.cvVolume(cvVolume[i]); - cvb.cvBasalArea(cvBasalArea[i]); - cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter[i]); - cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall[i]); - }); - }).toList() - ); + compatibilityVariables = Optional.of(IntStream.range(0, n).mapToObj(i -> { + if (cvVolume[i] == null) + return null; + return VdypCompatibilityVariables.build(cvb -> { + + cvb.cvVolume(cvVolume[i]); + cvb.cvBasalArea(cvBasalArea[i]); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter[i]); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall[i]); + }); + }).toList()); } public float getCVVolume( int speciesIndex, UtilizationClass uc, UtilizationClassVariable volumeVariable, LayerType layerType ) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvVolume(uc, volumeVariable, layerType); } public float getCVBasalArea(int speciesIndex, UtilizationClass uc, LayerType layerType) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvBasalArea(uc, layerType); } public float getCVQuadraticMeanDiameter(int speciesIndex, UtilizationClass uc, LayerType layerType) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvQuadraticMeanDiameter(uc, layerType); } public float getCVSmall(int speciesIndex, UtilizationClassVariable variable) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvPrimaryLayerSmall(variable); } public MatrixMap3 getCvVolume(int speciesIndex) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvVolume(); } public MatrixMap2 getCvBasalArea(int speciesIndex) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvBasalArea(); } public MatrixMap2 getCvQuadraticMeanDiameter(int speciesIndex) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvQuadraticMeanDiameter(); } public Map getCvPrimaryLayerSmall(int speciesIndex) { - return compatibilityVariables - .orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)) - .get(speciesIndex) + return compatibilityVariables.orElseThrow(() -> new IllegalStateException(UNSET_CV_VOLUMES)).get(speciesIndex) .getCvPrimaryLayerSmall(); } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java index c399029fd..238a4e4f4 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java @@ -324,8 +324,7 @@ public void updateCompatibilityVariablesAfterGrowth() { for (UtilizationClassVariable sucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { getCvPrimaryLayerSmall(i).put( sucv, - getCvPrimaryLayerSmall(i).get(sucv) - * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) + getCvPrimaryLayerSmall(i).get(sucv) * compVarAdjustments.getValue(UtilizationClass.SMALL, sucv) ); } for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { @@ -361,8 +360,7 @@ protected VdypLayer updateLayerFromBank() { VdypSpecies species = updatedLayer.getSpeciesBySp0(getBank().speciesNames[i]); species.setCompatibilityVariables( - getCvVolume(i), getCvBasalArea(i), getCvQuadraticMeanDiameter(i), - getCvPrimaryLayerSmall(i) + getCvVolume(i), getCvBasalArea(i), getCvQuadraticMeanDiameter(i), getCvPrimaryLayerSmall(i) ); } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java index b492392b4..e93293599 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java @@ -5,7 +5,6 @@ import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMapImpl; -import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; @@ -24,7 +23,7 @@ protected ForwardResolvedControlMap resolveControlMap(Map contro @Override protected ForwardLayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) throws ProcessingException { - return new ForwardLayerProcessingState(this, polygon, polygon.getLayers().get(LayerType.PRIMARY)); + return new ForwardLayerProcessingState(this, polygon, layer); } } \ No newline at end of file From 9253b839e7b63e260e6f870957992025019140e2 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Fri, 22 Nov 2024 23:32:07 -0800 Subject: [PATCH 38/45] Bank deep equals --- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 278 ++++------- .../bc/gov/nrs/vdyp/test/TestUtilsTest.java | 461 ++++++++++++++++++ .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 107 +++- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 14 +- 4 files changed, 668 insertions(+), 192 deletions(-) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 2dd52b464..db2e32332 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -62,14 +62,12 @@ import ca.bc.gov.nrs.vdyp.model.GenusDefinitionMap; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; -import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; -import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; public class TestUtils { @@ -647,140 +645,6 @@ public static BecDefinition mockBec() { return new BecDefinition("T", Region.COASTAL, "Test"); } - public static VdypPolygon deepCopy(VdypPolygon poly) { - var result = VdypPolygon.build(pb -> { - pb.polygonIdentifier(poly.getPolygonIdentifier().getBase(), poly.getPolygonIdentifier().getYear()); - pb.biogeoclimaticZone(poly.getBiogeoclimaticZone()); - pb.forestInventoryZone(poly.getForestInventoryZone()); - - pb.inventoryTypeGroup(poly.getInventoryTypeGroup()); - pb.targetYear(poly.getTargetYear()); - - pb.mode(poly.getMode()); - pb.percentAvailable(poly.getPercentAvailable()); - - for (var layer : poly.getLayers().values()) { - pb.addLayer(lb -> { - lb.layerType(layer.getLayerType()); - - lb.empiricalRelationshipParameterIndex(layer.getEmpiricalRelationshipParameterIndex()); - - lb.inventoryTypeGroup(layer.getInventoryTypeGroup()); - - lb.loreyHeight(layer.getLoreyHeightByUtilization()); - lb.treesPerHectare(layer.getTreesPerHectareByUtilization()); - lb.quadMeanDiameter(layer.getQuadraticMeanDiameterByUtilization()); - lb.baseArea(layer.getBaseAreaByUtilization()); - - lb.wholeStemVolume(layer.getWholeStemVolumeByUtilization()); - lb.closeUtilizationVolumeByUtilization(layer.getCloseUtilizationVolumeByUtilization()); - lb.closeUtilizationVolumeNetOfDecayByUtilization(layer.getCloseUtilizationVolumeByUtilization()); - lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( - layer.getCloseUtilizationVolumeNetOfDecayByUtilization() - ); - lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() - ); - - for (var spec : layer.getSpecies().values()) { - lb.addSpecies(sb -> { - sb.genus(spec.getGenus()); - sb.genus(spec.getGenusIndex()); - - sb.breakageGroup(spec.getBreakageGroup()); - sb.volumeGroup(spec.getVolumeGroup()); - sb.decayGroup(spec.getDecayGroup()); - - sb.percentGenus(spec.getPercentGenus()); - - sb.loreyHeight(layer.getLoreyHeightByUtilization()); - sb.treesPerHectare(layer.getTreesPerHectareByUtilization()); - sb.quadMeanDiameter(layer.getQuadraticMeanDiameterByUtilization()); - sb.baseArea(layer.getBaseAreaByUtilization()); - - sb.wholeStemVolume(layer.getWholeStemVolumeByUtilization()); - sb.closeUtilizationVolumeByUtilization(layer.getCloseUtilizationVolumeByUtilization()); - sb.closeUtilizationVolumeNetOfDecayByUtilization( - layer.getCloseUtilizationVolumeByUtilization() - ); - sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( - layer.getCloseUtilizationVolumeNetOfDecayByUtilization() - ); - sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization() - ); - - spec.getCompatibilityVariables().ifPresent(cv -> { - - sb.addCompatibilityVariables(cvb -> { - - MatrixMap3Impl cvVolume = new MatrixMap3Impl<>( - UtilizationClass.UTIL_CLASSES, - VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, - (uc, vv, lt) -> cv.getCvVolume(uc, vv, lt) - ); - MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, - (uc, lt) -> cv.getCvBasalArea(uc, lt) - ); - MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>( - UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, - (uc, lt) -> cv.getCvQuadraticMeanDiameter(uc, lt) - ); - Map cvPrimaryLayerSmall = new HashMap<>(); - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var vv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - for (var lt : LayerType.ALL_USED) { - cvVolume.put(uc, vv, lt, cv.getCvVolume(uc, vv, lt)); - } - } - } - - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var lt : LayerType.ALL_USED) { - cvBasalArea.put(uc, lt, cv.getCvBasalArea(uc, lt)); - } - } - - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var lt : LayerType.ALL_USED) { - cvQuadraticMeanDiameter.put(uc, lt, cv.getCvQuadraticMeanDiameter(uc, lt)); - } - } - for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv)); - } - - cvb.cvVolume(cvVolume); - cvb.cvBasalArea(cvBasalArea); - cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); - cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); - }); - - }); - - spec.getSite().ifPresent(site -> { - - sb.addSite(ib -> { - ib.ageTotal(site.getAgeTotal()); - ib.height(site.getHeight()); - ib.siteCurveNumber(site.getSiteCurveNumber()); - ib.siteIndex(site.getSiteIndex()); - ib.yearsToBreastHeight(site.getYearsToBreastHeight()); - }); - - }); - - }); - } - - lb.primaryGenus(layer.getPrimaryGenus()); - }); - } - }); - return result; - } - private static void line(Appendable out, String line, Object... args) throws UncheckedIOException { try { out.append(String.format(line, args)).append("\n"); @@ -795,7 +659,7 @@ private static String stringLiteral(String s) { private static String utilVectorLiteral(UtilizationVector uv) { if (uv.size() == 2) { - return String.format("Utils.heightVector(%f, %f)", uv.getSmall(), uv.getAll()); + return String.format("Utils.heightVector(%ff, %ff)", uv.getSmall(), uv.getAll()); } if (uv.get(UtilizationClass.SMALL) == 0 && uv.get(UtilizationClass.U75TO125) == 0 && uv.get(UtilizationClass.U125TO175) == 0 && uv.get(UtilizationClass.U175TO225) == 0 @@ -804,14 +668,14 @@ private static String utilVectorLiteral(UtilizationVector uv) { } if (Math.abs(UtilizationClass.ALL_CLASSES.stream().mapToDouble(uv::get).sum() - uv.getAll()) < 0.00001) { return String.format( - "Utils.utilizationVector(%f, %f, %f, %f, %f)", uv.getSmall(), uv.get(UtilizationClass.U75TO125), - uv.get(UtilizationClass.U125TO175), uv.get(UtilizationClass.U175TO225), - uv.get(UtilizationClass.OVER225) + "Utils.utilizationVector(%ff, %ff, %ff, %ff, %ff)", uv.getSmall(), + uv.get(UtilizationClass.U75TO125), uv.get(UtilizationClass.U125TO175), + uv.get(UtilizationClass.U175TO225), uv.get(UtilizationClass.OVER225) ); } return String.format( - "Utils.utilizationVector(%f, %f, %f, %f, %f, %f) /* ALL does not match sum of bands */", uv.getSmall(), - uv.getAll(), uv.get(UtilizationClass.U75TO125), uv.get(UtilizationClass.U125TO175), + "Utils.utilizationVector(%ff, %ff, %ff, %ff, %ff, %ff) /* ALL does not match sum of bands */", + uv.getSmall(), uv.getAll(), uv.get(UtilizationClass.U75TO125), uv.get(UtilizationClass.U125TO175), uv.get(UtilizationClass.U175TO225), uv.get(UtilizationClass.OVER225) ); @@ -822,6 +686,14 @@ private static String enumLiteral(Enum e) { return String.format("%s.%s", e.getClass().getName(), e.toString()); } + private static String floatLiteral(float v) { + return String.format("%ff", v); + } + + private static String intLiteral(int v) { + return String.format("%d", v); + } + private static String optionalLiteral(Optional opt, Function valueLiteral) { return opt.map(valueLiteral).map(s -> String.format("Optional.of(%s)", s)).orElse("Optional.empty()"); } @@ -830,11 +702,11 @@ private static String optionalLiteral(Optional opt, Function v * Serializes a VdypPolygon as Java code that can be executed to recreate it. Meant to be used to aid in creating * unit tests. */ - public static void write(VdypPolygon poly, Appendable out) throws IOException { + public static void writeModel(VdypPolygon poly, Appendable out, String assignTo) throws IOException { try { line(out, "/* the following Polygon definition was generated */"); line(out, ""); - line(out, "var result = VdypPolygon.build(pb -> {"); + line(out, "%s = VdypPolygon.build(pb -> {", assignTo); line( out, " pb.polygonIdentifier(%s, %d);", stringLiteral(poly.getPolygonIdentifier().getBase()), @@ -842,14 +714,20 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { ); line(out, ""); - line(out, " pb.biogeoclimaticZone(getBec(%s));", stringLiteral(poly.getBiogeoclimaticZone().getAlias())); + line( + out, " pb.biogeoclimaticZone(Utils.getBec(%s, controlMap));", + stringLiteral(poly.getBiogeoclimaticZone().getAlias()) + ); line(out, " pb.forestInventoryZone(%s);", stringLiteral(poly.getForestInventoryZone())); line(out, ""); - line(out, " pb.inventoryTypeGroup(%d);", poly.getInventoryTypeGroup()); - line(out, " pb.targetYear(%d);", poly.getTargetYear()); + line( + out, " pb.inventoryTypeGroup(%s);", + optionalLiteral(poly.getInventoryTypeGroup(), TestUtils::intLiteral) + ); + line(out, " pb.targetYear(%s);", optionalLiteral(poly.getTargetYear(), TestUtils::intLiteral)); line(out, ""); line(out, " pb.mode(%s);", optionalLiteral(poly.getMode(), TestUtils::enumLiteral)); - line(out, " pb.percentAvailable(%f);", poly.getPercentAvailable()); + line(out, " pb.percentAvailable(%s);", floatLiteral(poly.getPercentAvailable())); line(out, ""); for (var layer : poly.getLayers().values()) { line(out, " pb.addLayer(lb -> {"); @@ -857,12 +735,12 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, ""); line( out, " lb.empiricalRelationshipParameterIndex(%s);", - optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), i -> Integer.toString(i)) + optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), TestUtils::intLiteral) ); line(out, ""); line( out, " lb.inventoryTypeGroup(%s);", - optionalLiteral(layer.getInventoryTypeGroup(), i -> Integer.toString(i)) + optionalLiteral(layer.getInventoryTypeGroup(), TestUtils::intLiteral) ); line(out, ""); line(out, " lb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); @@ -893,45 +771,54 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, ""); for (var spec : layer.getSpecies().values()) { line(out, " lb.addSpecies(sb -> {"); - line(out, " sb.genus(%s);", spec.getGenus()); + line(out, " sb.genus(%s);", stringLiteral(spec.getGenus())); line(out, " sb.genus(%d);", spec.getGenusIndex()); line(out, ""); line(out, " sb.breakageGroup(%d);", spec.getBreakageGroup()); line(out, " sb.volumeGroup(%d);", spec.getVolumeGroup()); line(out, " sb.decayGroup(%d);", spec.getDecayGroup()); line(out, ""); - line(out, " sb.percentGenus(%f);", spec.getPercentGenus()); + line(out, " sb.percentGenus(%s);", floatLiteral(spec.getPercentGenus())); + line(out, ""); + + spec.getSp64DistributionSet().getSp64DistributionList().forEach(sp64 -> { + line( + out, " sb.addSp64Distribution(%s, %s);", stringLiteral(sp64.getGenusAlias()), + floatLiteral(sp64.getPercentage()) + ); + }); + line(out, ""); - line(out, " sb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); + line(out, " sb.loreyHeight(%s);", utilVectorLiteral(spec.getLoreyHeightByUtilization())); line( out, " sb.treesPerHectare(%s);", - utilVectorLiteral(layer.getTreesPerHectareByUtilization()) + utilVectorLiteral(spec.getTreesPerHectareByUtilization()) ); line( out, " sb.quadMeanDiameter(%s);", - utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) + utilVectorLiteral(spec.getQuadraticMeanDiameterByUtilization()) ); - line(out, " sb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); + line(out, " sb.baseArea(%s);", utilVectorLiteral(spec.getBaseAreaByUtilization())); line(out, ""); line( out, " sb.wholeStemVolume(%s);", - utilVectorLiteral(layer.getWholeStemVolumeByUtilization()) + utilVectorLiteral(spec.getWholeStemVolumeByUtilization()) ); line( out, " sb.closeUtilizationVolumeByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + utilVectorLiteral(spec.getCloseUtilizationVolumeByUtilization()) ); line( - out, " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s));", - utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + out, " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayByUtilization()) ); line( out, " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) ); line( out, " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) ); line(out, ""); spec.getCompatibilityVariables().ifPresent(cv -> { @@ -940,24 +827,27 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line( out, - " MatrixMap3Impl cvVolume = new MatrixMap3Impl<>(" + " MatrixMap3 cvVolume = new MatrixMap3Impl<>(" ); line(out, " UtilizationClass.UTIL_CLASSES, "); - line(out, " VolumeVariable.ALL, "); + line(out, " VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, "); line(out, " LayerType.ALL_USED,"); line(out, " (uc, vv, lt) -> 0f"); line(out, " );"); for (var uc : UtilizationClass.UTIL_CLASSES) { for (var vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { for (var lt : LayerType.ALL_USED) { - line(out, " cvVolume.put(uc, vv, lt, %f);", cv.getCvVolume(uc, vv, lt)); + line( + out, + " cvVolume.put(UtilizationClass.%s, UtilizationClassVariable.%s, LayerType.%s, %s);", + uc, vv, lt, floatLiteral(cv.getCvVolume(uc, vv, lt)) + ); } } } - line(out, " );"); line( out, - " MatrixMap2Impl cvBasalArea = new MatrixMap2Impl<>(" + " MatrixMap2 cvBasalArea = new MatrixMap2Impl<>(" ); line(out, " UtilizationClass.UTIL_CLASSES, "); line(out, " LayerType.ALL_USED,"); @@ -966,14 +856,16 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { for (var uc : UtilizationClass.UTIL_CLASSES) { for (var lt : LayerType.ALL_USED) { - line(out, " cvBasalArea.put(uc, lt, %f);", cv.getCvBasalArea(uc, lt)); + line( + out, " cvBasalArea.put(UtilizationClass.%s, LayerType.%s, %s);", + uc, lt, floatLiteral(cv.getCvBasalArea(uc, lt)) + ); } } - line(out, " );"); line( out, - " MatrixMap2Impl cvQuadraticMeanDiameter = new MatrixMap2Impl<>(" + " MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>(" ); line(out, " UtilizationClass.UTIL_CLASSES, "); line(out, " LayerType.ALL_USED,"); @@ -983,12 +875,12 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { for (var uc : UtilizationClass.UTIL_CLASSES) { for (var lt : LayerType.ALL_USED) { line( - out, " cvQuadraticMeanDiameter.put(uc, lt, %f);", - cv.getCvQuadraticMeanDiameter(uc, lt) + out, + " cvQuadraticMeanDiameter.put(UtilizationClass.%s, LayerType.%s, %s);", + uc, lt, floatLiteral(cv.getCvQuadraticMeanDiameter(uc, lt)) ); } } - line(out, " );"); line(out, ""); line( out, @@ -996,7 +888,10 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { ); line(out, ""); for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - line(out, " cvPrimaryLayerSmall.put(ucv, cv.getCvPrimaryLayerSmall(ucv));"); + line( + out, " cvPrimaryLayerSmall.put(UtilizationClassVariable.%s, %s);", ucv, + floatLiteral(cv.getCvPrimaryLayerSmall(ucv)) + ); } line(out, ""); line(out, " cvb.cvVolume(cvVolume);"); @@ -1004,27 +899,44 @@ public static void write(VdypPolygon poly, Appendable out) throws IOException { line(out, " cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter);"); line(out, " cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall);"); line(out, " });"); - line(out, ""); }); line(out, ""); spec.getSite().ifPresent(site -> { line(out, ""); line(out, " sb.addSite(ib -> {"); - line(out, " ib.ageTotal(%f);", site.getAgeTotal()); - line(out, " ib.height(%f);", site.getHeight()); - line(out, " ib.siteCurveNumber(%d);", site.getSiteCurveNumber()); - line(out, " ib.siteIndex(%f);", site.getSiteIndex()); - line(out, " ib.yearsToBreastHeight(%f);", site.getYearsToBreastHeight()); + line( + out, " ib.ageTotal(%s);", + optionalLiteral(site.getAgeTotal(), TestUtils::floatLiteral) + ); + line( + out, " ib.height(%s);", + optionalLiteral(site.getHeight(), TestUtils::floatLiteral) + ); + line( + out, " ib.siteCurveNumber(%s);", + optionalLiteral(site.getSiteCurveNumber(), TestUtils::intLiteral) + ); + line( + out, " ib.siteIndex(%s);", + optionalLiteral(site.getSiteIndex(), TestUtils::floatLiteral) + ); + line( + out, " ib.yearsToBreastHeight(%s);", + optionalLiteral(site.getYearsToBreastHeight(), TestUtils::floatLiteral) + ); line(out, " });"); line(out, ""); }); line(out, ""); - line(out, " });"); + line(out, " });"); } line(out, ""); - line(out, "lb.primaryGenus(%s);", optionalLiteral(layer.getPrimaryGenus(), TestUtils::stringLiteral)); - line(out, "});"); + line( + out, " lb.primaryGenus(%s);", + optionalLiteral(layer.getPrimaryGenus(), TestUtils::stringLiteral) + ); + line(out, " });"); } line(out, "});"); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java new file mode 100644 index 000000000..575951c07 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java @@ -0,0 +1,461 @@ +package ca.bc.gov.nrs.vdyp.test; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.deepEquals; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; + +public class TestUtilsTest { + + @Nested + class WriteModel { + Random rand; + + @BeforeEach + void setup() { + rand = new Random(42); + } + + UtilizationVector mockUtilVector(float multiplier) { + return Utils.utilizationVector( + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier + ); + } + + UtilizationVector mockHeightVector() { + return Utils.heightVector(rand.nextFloat() * 5, rand.nextFloat() * 20); + } + + @Test + void polygon() throws IOException { + Random rand = new Random(42); + var controlMap = TestUtils.loadControlMap(); + + var poly = VdypPolygon.build(pb -> { + + pb.polygonIdentifier("Test", 2024); + pb.percentAvailable(90f); + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.addLayer(lb -> { + + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + }); + + var buf = new StringBuffer(); + TestUtils.writeModel(poly, buf, "result"); + + System.out.print(buf.toString()); + + VdypPolygon result = get(controlMap); + + assertThat(result, deepEquals(poly)); + } + + private VdypPolygon get(Map controlMap) { + VdypPolygon result = null; + + /* + * There is probably a way to automate this but it would require some additional work in Maven. + * + * For now, if something changes how the code generation works, Run the test, copy the output to Stdout to + * replace the code delimited by the following comments and run again to confirm the test passes. + */ + + /* the following Polygon definition was generated */ + + result = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.inventoryTypeGroup(Optional.empty()); + pb.targetYear(Optional.empty()); + + pb.mode(Optional.empty()); + pb.percentAvailable(90.000000f); + + pb.addLayer(lb -> { + lb.layerType(ca.bc.gov.nrs.vdyp.model.LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(Optional.of(21)); + + lb.inventoryTypeGroup(Optional.of(34)); + + lb.loreyHeight(Utils.heightVector(3.637818f, 1.093304f)); + lb.treesPerHectare( + Utils.utilizationVector( + 110.634865f, 543.441101f, 114.492920f, 82.724403f, 207.127731f, 139.096069f + ) /* ALL does not match sum of bands */ + ); + lb.quadMeanDiameter( + Utils.utilizationVector( + 7.077106f, 21.115028f, 6.655489f, 0.913246f, 9.033722f, 4.512572f + ) /* ALL does not match sum of bands */ + ); + lb.baseArea( + Utils.utilizationVector( + 1.366447f, 3.151621f, 0.095879f, 0.617439f, 1.884147f, 0.554157f + ) /* ALL does not match sum of bands */ + ); + + lb.wholeStemVolume( + Utils.utilizationVector( + 5.334631f, 19.966562f, 5.480312f, 6.987250f, 6.435294f, 1.063708f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 2.618946f, 15.041424f, 2.638799f, 4.499437f, 5.583787f, 2.319401f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 3.991433f, 6.302918f, 0.886892f, 0.752737f, 2.971749f, 1.691540f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.839070f, 6.456025f, 1.002645f, 3.303863f, 1.460645f, 0.688872f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.475964f, 6.769507f, 1.762282f, 0.822518f, 2.253841f, 1.930866f + ) /* ALL does not match sum of bands */ + ); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.genus(10); + + sb.breakageGroup(12); + sb.volumeGroup(14); + sb.decayGroup(13); + + sb.percentGenus(90.000000f); + + sb.addSp64Distribution("MB", 100.000000f); + + sb.loreyHeight(Utils.heightVector(2.855202f, 1.605718f)); + sb.treesPerHectare( + Utils.utilizationVector( + 125.306259f, 761.289429f, 230.860580f, 292.210693f, 24.196316f, 214.021866f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 3.165325f, 13.858646f, 3.579199f, 1.990815f, 8.177969f, 0.110663f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 1.160050f, 3.346419f, 0.181329f, 1.505020f, 1.597234f, 0.062836f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 3.731056f, 13.056453f, 3.364021f, 6.114161f, 2.041595f, 1.536676f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 5.699161f, 13.450598f, 3.575303f, 4.922951f, 1.132478f, 3.819867f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 1.892143f, 6.510414f, 1.845608f, 0.682118f, 1.801274f, 2.181414f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 1.738644f, 9.099073f, 2.595175f, 1.829268f, 2.783877f, 1.890754f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 2.852798f, 5.647935f, 1.407068f, 1.646782f, 2.481967f, 0.112118f + ) /* ALL does not match sum of bands */ + ); + + sb.addCompatibilityVariables(cvb -> { + + MatrixMap3 cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, + VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, + (uc, vv, lt) -> 0f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 7.275637f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 0.546652f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 6.832234f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 0.479393f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 3.087194f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 9.420735f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + 2.770784f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.VETERAN, + 7.077106f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 6.655489f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 0.913246f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 9.033722f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 4.512572f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 3.687829f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 3.816431f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + 2.757480f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.VETERAN, + 6.904258f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 4.636536f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 7.620902f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 7.829018f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 9.981786f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 9.193277f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 1.519582f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + 4.364910f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.VETERAN, + 4.397998f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 7.499061f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 9.306312f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 3.865668f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 7.982866f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 1.773785f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 1.505474f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + 5.943499f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.VETERAN, + 3.383079f + ); + MatrixMap2 cvBasalArea = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 2.097675f); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.VETERAN, 2.506613f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 8.259658f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.VETERAN, 3.651612f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 1.722179f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.VETERAN, 1.586545f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.PRIMARY, 5.874274f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.VETERAN, 2.741725f); + MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 7.512804f); + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.VETERAN, 6.436222f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 5.710403f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.VETERAN, 0.802859f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 5.800248f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.VETERAN, 0.906644f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.PRIMARY, 7.525099f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.VETERAN, 7.986169f); + + Map cvPrimaryLayerSmall = new HashMap<>(); + + cvPrimaryLayerSmall.put(UtilizationClassVariable.LOREY_HEIGHT, 0.314182f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.BASAL_AREA, 3.165325f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 3.579199f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.WHOLE_STEM_VOL, 1.990815f); + + cvb.cvVolume(cvVolume); + cvb.cvBasalArea(cvBasalArea); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); + }); + + sb.addSite(ib -> { + ib.ageTotal(Optional.of(40.000000f)); + ib.height(Optional.of(15.000000f)); + ib.siteCurveNumber(Optional.of(42)); + ib.siteIndex(Optional.of(4.000000f)); + ib.yearsToBreastHeight(Optional.of(5.000000f)); + }); + + }); + + lb.primaryGenus(Optional.of("MB")); + }); + }); + + /* End of generated polygon Definition */ + return result; + } + } +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index ed7098b9c..685fba673 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -3,8 +3,10 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anything; +import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.describedAs; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; @@ -14,6 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -25,6 +28,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.stream.Stream; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -32,6 +36,7 @@ import org.hamcrest.Matchers; import org.hamcrest.StringDescription; import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.junit.platform.commons.util.ReflectionUtils; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; @@ -46,6 +51,7 @@ import ca.bc.gov.nrs.vdyp.model.Coefficients; import ca.bc.gov.nrs.vdyp.model.MatrixMap; import ca.bc.gov.nrs.vdyp.model.PolygonIdentifier; +import ca.bc.gov.nrs.vdyp.model.Sp64DistributionSet; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; @@ -55,14 +61,16 @@ import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; /** * Custom Hamcrest Matchers * * @author Kevin Smith, Vivid Solutions + * @param * */ -public class VdypMatchers { +public class VdypMatchers { static final float EPSILON = 0.001f; @@ -1253,4 +1261,101 @@ protected boolean matchesSafely(VdypSite item, Description mismatchDescription) } }; } + + public static Matcher hasField(String name, Matcher valueMatcher) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + + description.appendText("has field named ").appendValue(name).appendText(" that "); + valueMatcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(T item, Description mismatchDescription) { + try { + var field = item.getClass().getField(name); + var value = ReflectionUtils.tryToReadFieldValue(field, item); + if (!valueMatcher.matches(value)) { + mismatchDescription.appendText("field ").appendValue(name).appendText(" "); + return false; + } + return true; + } catch (NoSuchFieldException e) { + mismatchDescription.appendText("field ").appendValue(name).appendText(" does not exist."); + return false; + } catch (SecurityException e) { + mismatchDescription.appendText("field ").appendValue(name).appendText(" is not accessible."); + return false; + } + } + }; + } + + @SuppressWarnings("unchecked") + private static V getField(String name, Object item) { + Field field; + try { + field = item.getClass().getField(name); + return (V) field.get(item); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + throw new IllegalStateException(e); + } + } + + @SuppressWarnings("unchecked") + public static Matcher arrayCloseTo1D(Float[] expected) { + @SuppressWarnings("rawtypes") + final List list = Arrays.stream(expected).map(f -> closeTo(f)).toList(); + return Matchers.arrayContaining(list); + } + + @SuppressWarnings("unchecked") + public static Matcher arrayCloseTo2D(Float[][] expected) { + @SuppressWarnings("rawtypes") + final List list = Arrays.stream(expected).map(VdypMatchers::arrayCloseTo1D).toList(); + return arrayContaining(list); + } + + @SuppressWarnings("unchecked") + public static Matcher arrayCloseTo3D(Float[][][] expected) { + @SuppressWarnings("rawtypes") + final List list = Arrays.stream(expected).map(VdypMatchers::arrayCloseTo2D).toList(); + return arrayContaining(list); + } + + public static Matcher deepEquals(Bank expected) { + + Stream> otherArrays = Stream.of( + "speciesNames", + "sp64Distributions", + "siteCurveNumbers", + "speciesIndices" + ).map(name -> hasField(name, equalTo(getField(name, expected)))); + + Stream> floatArrayMatchers1d = Stream.of( + "siteIndices", + "dominantHeights", + "ageTotals", + "yearsAtBreastHeight", + "yearsToBreastHeight", + "percentagesOfForestedLand" + ).map(name -> hasField(name, arrayCloseTo1D(getField(name, expected)))); + + Stream> floatArrayMatchers2d = Stream.of( + "loreyHeights", + "basalAreas", + "closeUtilizationVolumes", + "cuVolumesMinusDecay", + "cuVolumesMinusDecayAndWastage", + "quadMeanDiameters", + "treesPerHectare", + "wholeStemVolumes" + ).map(name -> hasField(name, arrayCloseTo2D(getField(name, expected)))); + + Stream> floatArrayMatchers = Stream.concat(floatArrayMatchers1d, floatArrayMatchers1d); + + return allOf((List) floatArrayMatchers.toList()); + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 509111095..a7a2cbc4c 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -15,13 +15,11 @@ import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.hamcrest.StringDescription; -import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.LayerType; @@ -55,7 +53,12 @@ static void assertMatch(T item, Matcher unit) { assertTrue(result, "Expected match to pass but it failed with description: " + description.toString()); } - Random rand = new Random(42); + Random rand; + + @BeforeEach + void setup() { + rand = new Random(42); + } UtilizationVector mockUtilVector(float multiplier) { return Utils.utilizationVector( @@ -78,7 +81,6 @@ class testVdypPolygon { @BeforeEach void setup() { - Random rand = new Random(42); controlMap = TestUtils.loadControlMap(); expected = VdypPolygon.build(pb -> { @@ -283,7 +285,6 @@ class testVdypLayer { @BeforeEach void setup() { - Random rand = new Random(42); var controlMap = TestUtils.loadControlMap(); // The numbers don't add up, we are just using them to test comparison @@ -522,7 +523,6 @@ class testVdypSpecies { @BeforeEach void setup() { - Random rand = new Random(42); var controlMap = TestUtils.loadControlMap(); // The numbers don't add up, we are just using them to test comparison @@ -803,8 +803,6 @@ class testVdypCompatibilityVariables { @BeforeEach void setup() { - // Use a fixed seed so the random numbers are the same across runs. - Random rand = new Random(42); expected = VdypCompatibilityVariables.build(cvb -> { cvb.polygonIdentifier("Test", 2024); From 7ddbd49ca5c711660aae0aa2b2ab4439abb65cc4 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 26 Nov 2024 18:12:08 -0800 Subject: [PATCH 39/45] Deep Equals for Bank --- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 90 +++++-- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 226 ++++++++++++++++++ 2 files changed, 296 insertions(+), 20 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 685fba673..7393af4d3 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -30,6 +30,7 @@ import java.util.function.Function; import java.util.stream.Stream; +import org.apache.commons.lang3.ArrayUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -1268,7 +1269,7 @@ public static Matcher hasField(String name, Matcher valueMatcher) { @Override public void describeTo(Description description) { - description.appendText("has field named ").appendValue(name).appendText(" that "); + description.appendText("has field named ").appendValue(name).appendText(" that is "); valueMatcher.describeTo(description); } @@ -1276,16 +1277,36 @@ public void describeTo(Description description) { protected boolean matchesSafely(T item, Description mismatchDescription) { try { var field = item.getClass().getField(name); - var value = ReflectionUtils.tryToReadFieldValue(field, item); + var value = field.get(item); + var arrayComponentType = value.getClass().componentType(); + + // Hamcrest does not behave nicely with arrays of primitive types so we need to convert to arrays of boxed types + if (arrayComponentType == int.class) { + value = ArrayUtils.toObject((int[]) value); + } else if (arrayComponentType == float.class) { + value = ArrayUtils.toObject((float[]) value); + } else if (arrayComponentType.isPrimitive()) { + throw new IllegalStateException("Don't know how to unbox an array of " + arrayComponentType); + } else if (arrayComponentType.isArray()) { + if (arrayComponentType.getComponentType() == float.class) { + value = Arrays.stream((float[][]) value).map(ArrayUtils::toObject).toArray(); + } else if (arrayComponentType.getComponentType().isPrimitive()) { + throw new IllegalStateException( + "Don't know how to unbox a 2D array of " + arrayComponentType + ); + } + } + if (!valueMatcher.matches(value)) { mismatchDescription.appendText("field ").appendValue(name).appendText(" "); + valueMatcher.describeMismatch(value, mismatchDescription); return false; } return true; } catch (NoSuchFieldException e) { mismatchDescription.appendText("field ").appendValue(name).appendText(" does not exist."); return false; - } catch (SecurityException e) { + } catch (IllegalAccessException e) { mismatchDescription.appendText("field ").appendValue(name).appendText(" is not accessible."); return false; } @@ -1294,7 +1315,7 @@ protected boolean matchesSafely(T item, Description mismatchDescription) { } @SuppressWarnings("unchecked") - private static V getField(String name, Object item) { + static V getField(String name, Object item) { Field field; try { field = item.getClass().getField(name); @@ -1304,22 +1325,30 @@ private static V getField(String name, Object item) { } } + + @SuppressWarnings("unchecked") + public static Matcher array1D(int[] expected) { + @SuppressWarnings("rawtypes") + final List list = Arrays.stream(ArrayUtils.toObject(expected)).map(i -> equalTo(i)).toList(); + return arrayContaining(list); + } + @SuppressWarnings("unchecked") - public static Matcher arrayCloseTo1D(Float[] expected) { + public static Matcher arrayCloseTo1D(float[] expected) { @SuppressWarnings("rawtypes") - final List list = Arrays.stream(expected).map(f -> closeTo(f)).toList(); - return Matchers.arrayContaining(list); + final List list = Arrays.stream(ArrayUtils.toObject(expected)).map(f -> closeTo(f)).toList(); + return arrayContaining(list); } @SuppressWarnings("unchecked") - public static Matcher arrayCloseTo2D(Float[][] expected) { + public static Matcher arrayCloseTo2D(float[][] expected) { @SuppressWarnings("rawtypes") final List list = Arrays.stream(expected).map(VdypMatchers::arrayCloseTo1D).toList(); return arrayContaining(list); } @SuppressWarnings("unchecked") - public static Matcher arrayCloseTo3D(Float[][][] expected) { + public static Matcher arrayCloseTo3D(float[][][] expected) { @SuppressWarnings("rawtypes") final List list = Arrays.stream(expected).map(VdypMatchers::arrayCloseTo2D).toList(); return arrayContaining(list); @@ -1327,23 +1356,44 @@ public static Matcher arrayCloseTo3D(Float[][][] expected) { public static Matcher deepEquals(Bank expected) { - Stream> otherArrays = Stream.of( - "speciesNames", - "sp64Distributions", + List> matchers = new LinkedList<>(); + Stream.of( "siteCurveNumbers", "speciesIndices" - ).map(name -> hasField(name, equalTo(getField(name, expected)))); + ) + .map( + name -> hasField( + name, array1D(VdypMatchers.getField(name, expected)) + ) - Stream> floatArrayMatchers1d = Stream.of( + ) + .forEach(matchers::add); + + Stream.of( + "speciesNames", + "sp64Distributions" + ) + .map( + name -> hasField( + name, Matchers.arrayContaining( + (Object[]) getField(name, expected) + ) + ) + ) + .forEach(matchers::add); + + Stream.of( "siteIndices", "dominantHeights", "ageTotals", "yearsAtBreastHeight", "yearsToBreastHeight", "percentagesOfForestedLand" - ).map(name -> hasField(name, arrayCloseTo1D(getField(name, expected)))); + ) + .map(name -> hasField(name, arrayCloseTo1D(getField(name, expected)))) + .forEach(matchers::add); - Stream> floatArrayMatchers2d = Stream.of( + Stream.of( "loreyHeights", "basalAreas", "closeUtilizationVolumes", @@ -1352,10 +1402,10 @@ public static Matcher deepEquals(Bank expected) { "quadMeanDiameters", "treesPerHectare", "wholeStemVolumes" - ).map(name -> hasField(name, arrayCloseTo2D(getField(name, expected)))); - - Stream> floatArrayMatchers = Stream.concat(floatArrayMatchers1d, floatArrayMatchers1d); + ) + .map(name -> hasField(name, arrayCloseTo2D(getField(name, expected)))) + .forEach(matchers::add); - return allOf((List) floatArrayMatchers.toList()); + return allOf(matchers); } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index a7a2cbc4c..3fdbff935 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -20,8 +20,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; @@ -35,6 +37,7 @@ import ca.bc.gov.nrs.vdyp.model.VdypSite; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; class VdypMatchersTest { @@ -73,6 +76,229 @@ UtilizationVector mockHeightVector() { @Nested class deepEquals { + @Nested + class testBank { + Bank expected; + VdypLayer expectedLayer; + BecDefinition expectedBec; + Matcher unit; + Map controlMap; + + @BeforeEach + void setup() { + controlMap = TestUtils.loadControlMap(); + expectedBec = Utils.getBec("CDF", controlMap); + expectedLayer = VdypLayer.build(lb -> { + lb.polygonIdentifier("Test", 2024); + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + expected = new Bank(expectedLayer, expectedBec, x -> true); + + expected.percentagesOfForestedLand[1] = 70; + + unit = VdypMatchers.deepEquals(expected); + } + + @Test + void testPass() { + var actual = new Bank(expected); + + assertMatch(actual, unit); + } + + @Test + void testAgeDifferent() { + var actual = new Bank(expected); + actual.ageTotals[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"ageTotals\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <40.0>\\] field \\\"ageTotals\\\" item 1: was a java.lang.Float \\(<41.0F>\\)" + ) + ); + } + + @Test + void testDomHeightDifferent() { + var actual = new Bank(expected); + actual.dominantHeights[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"dominantHeights\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <15.0>\\] field \\\"dominantHeights\\\" item 1: was a java.lang.Float \\(<16.0F>\\)" + ) + ); + } + + @Test + void testPercentForestDifferent() { + var actual = new Bank(expected); + actual.percentagesOfForestedLand[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"percentagesOfForestedLand\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <70.0>\\] field \\\"percentagesOfForestedLand\\\" item 1: was a java.lang.Float \\(<71.0F>\\)" + ) + ); + } + + @Test + void testSIDifferent() { + var actual = new Bank(expected); + actual.siteIndices[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"siteIndices\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <4.0>\\] field \\\"siteIndices\\\" item 1: was a java.lang.Float \\(<5.0F>\\)" + ) + ); + } + + @Test + void testYearsAtBHDifferent() { + var actual = new Bank(expected); + actual.yearsAtBreastHeight[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"yearsAtBreastHeight\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <35.0>\\] field \\\"yearsAtBreastHeight\\\" item 1: was a java.lang.Float \\(<36.0F>\\)" + ) + ); + } + + @Test + void testYearsToBHDifferent() { + var actual = new Bank(expected); + actual.yearsToBreastHeight[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"yearsToBreastHeight\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <5.0>\\] field \\\"yearsToBreastHeight\\\" item 1: was a java.lang.Float \\(<6.0F>\\)" + ) + ); + } + + @Test + void testSCNDifferent() { + var actual = new Bank(expected); + actual.siteCurveNumbers[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"siteCurveNumbers\\\" that is \\[<0>, <42>\\] field \\\"siteCurveNumbers\\\" item 1: was <43>" + ) + ); + } + + @Test + void testSpecIndexDifferent() { + var actual = new Bank(expected); + actual.speciesIndices[1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \\\"speciesIndices\\\" that is \\[<0>, <10>\\] field \\\"speciesIndices\\\" item 1: was <11>" + ) + ); + } + + @Test + void testSpecNameDifferent() { + var actual = new Bank(expected); + actual.speciesNames[1] = "X"; + + assertMismatch( + actual, unit, equalTo( + "has field named \"speciesNames\" that is [null, \"MB\"] field \"speciesNames\" item 1: was \"X\"" + ) + ); + } + + @ParameterizedTest + @ValueSource( + strings = { + "loreyHeights", + "basalAreas", + "closeUtilizationVolumes", + "cuVolumesMinusDecay", + "cuVolumesMinusDecayAndWastage", + "quadMeanDiameters", + "treesPerHectare", + "wholeStemVolumes" } + ) + void testUtilizationDifferent(String name) throws Exception { + var actual = new Bank(expected); + var field = Bank.class.getField(name); + + ((float[][]) field.get(actual))[1][1] += 1; + + assertMismatch( + actual, unit, matchesRegex( + "has field named \"" + name + "\" that is .+? field \"" + name + + "\" item 1: item 1: was a java\\.lang\\.Float \\(<\\d+\\.\\d+F>\\)" + ) + ); + } + + } + @Nested class testVdypPolygon { VdypPolygon expected; From 687280c6d4f90c9c4c4d8067883a5dd3d5e161d4 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 27 Nov 2024 17:13:11 -0800 Subject: [PATCH 40/45] Deep Equals for Layer State --- .../TestLayerProcessingState.java | 32 +++ .../processing_state/TestProcessingState.java | 28 ++ .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 69 ++--- .../gov/nrs/vdyp/test/VdypMatchersTest.java | 254 ++++++++++++++++-- 4 files changed, 324 insertions(+), 59 deletions(-) create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestLayerProcessingState.java create mode 100644 lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestProcessingState.java diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestLayerProcessingState.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestLayerProcessingState.java new file mode 100644 index 000000000..0a4f73453 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestLayerProcessingState.java @@ -0,0 +1,32 @@ +package ca.bc.gov.nrs.vdyp.processing_state; + +import java.util.function.Predicate; + +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; + +public class TestLayerProcessingState extends LayerProcessingState { + + protected TestLayerProcessingState( + ProcessingState ps, VdypPolygon polygon, + LayerType subjectLayerType + ) { + super(ps, polygon, subjectLayerType); + } + + @Override + protected Predicate getBankFilter() { + return X -> true; + } + + @Override + protected VdypLayer updateLayerFromBank() { + return getBank().buildLayerFromBank(); + } + +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestProcessingState.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestProcessingState.java new file mode 100644 index 000000000..55f0c7c38 --- /dev/null +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/processing_state/TestProcessingState.java @@ -0,0 +1,28 @@ +package ca.bc.gov.nrs.vdyp.processing_state; + +import java.util.Map; + +import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMapImpl; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; + +public class TestProcessingState extends ProcessingState { + + public TestProcessingState(Map controlMap) throws ProcessingException { + super(controlMap); + } + + @Override + protected ResolvedControlMap resolveControlMap(Map controlMap) { + return new ResolvedControlMapImpl(controlMap); + } + + @Override + protected TestLayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) + throws ProcessingException { + return new TestLayerProcessingState(this, this.getCurrentPolygon(), layer.getLayerType()); + } + +} diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 7393af4d3..9b07a761d 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -63,6 +63,7 @@ import ca.bc.gov.nrs.vdyp.model.builders.ModelClassBuilder; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; /** * Custom Hamcrest Matchers @@ -628,6 +629,10 @@ public static Matcher isPolyId(String base, int year) { return allOf(instanceOf(PolygonIdentifier.class), hasProperty("base", is(base)), hasProperty("year", is(year))); } + public static Matcher isPolyId(PolygonIdentifier expected) { + return isPolyId(expected.getBase(), expected.getYear()); + } + public static Matcher isBec(String alias) { return allOf(instanceOf(BecDefinition.class), hasProperty("alias", is(alias))); } @@ -1280,7 +1285,8 @@ protected boolean matchesSafely(T item, Description mismatchDescription) { var value = field.get(item); var arrayComponentType = value.getClass().componentType(); - // Hamcrest does not behave nicely with arrays of primitive types so we need to convert to arrays of boxed types + // Hamcrest does not behave nicely with arrays of primitive types so we need to convert to arrays of + // boxed types if (arrayComponentType == int.class) { value = ArrayUtils.toObject((int[]) value); } else if (arrayComponentType == float.class) { @@ -1325,7 +1331,6 @@ static V getField(String name, Object item) { } } - @SuppressWarnings("unchecked") public static Matcher array1D(int[] expected) { @SuppressWarnings("rawtypes") @@ -1354,57 +1359,39 @@ public static Matcher arrayCloseTo3D(float[][][] expected) { return arrayContaining(list); } + public static > Matcher deepEquals(T expected) { + expected.getBank(); + return allOf( + hasProperty( + "polygon", + hasProperty("polygonIdentifier", isPolyId(expected.getPolygon().getPolygonIdentifier())) + ), hasProperty("layerType", equalTo(expected.getLayerType())), + hasProperty("bank", deepEquals(expected.getBank())) + ); + } + public static Matcher deepEquals(Bank expected) { List> matchers = new LinkedList<>(); - Stream.of( - "siteCurveNumbers", - "speciesIndices" - ) + Stream.of("siteCurveNumbers", "speciesIndices") .map( - name -> hasField( - name, array1D(VdypMatchers.getField(name, expected)) - ) + name -> hasField(name, array1D(VdypMatchers.getField(name, expected))) - ) - .forEach(matchers::add); + ).forEach(matchers::add); - Stream.of( - "speciesNames", - "sp64Distributions" - ) - .map( - name -> hasField( - name, Matchers.arrayContaining( - (Object[]) getField(name, expected) - ) - ) - ) + Stream.of("speciesNames", "sp64Distributions") + .map(name -> hasField(name, Matchers.arrayContaining((Object[]) getField(name, expected)))) .forEach(matchers::add); Stream.of( - "siteIndices", - "dominantHeights", - "ageTotals", - "yearsAtBreastHeight", - "yearsToBreastHeight", + "siteIndices", "dominantHeights", "ageTotals", "yearsAtBreastHeight", "yearsToBreastHeight", "percentagesOfForestedLand" - ) - .map(name -> hasField(name, arrayCloseTo1D(getField(name, expected)))) - .forEach(matchers::add); + ).map(name -> hasField(name, arrayCloseTo1D(getField(name, expected)))).forEach(matchers::add); Stream.of( - "loreyHeights", - "basalAreas", - "closeUtilizationVolumes", - "cuVolumesMinusDecay", - "cuVolumesMinusDecayAndWastage", - "quadMeanDiameters", - "treesPerHectare", - "wholeStemVolumes" - ) - .map(name -> hasField(name, arrayCloseTo2D(getField(name, expected)))) - .forEach(matchers::add); + "loreyHeights", "basalAreas", "closeUtilizationVolumes", "cuVolumesMinusDecay", + "cuVolumesMinusDecayAndWastage", "quadMeanDiameters", "treesPerHectare", "wholeStemVolumes" + ).map(name -> hasField(name, arrayCloseTo2D(getField(name, expected)))).forEach(matchers::add); return allOf(matchers); } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 3fdbff935..0b529811f 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -22,7 +22,10 @@ import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.ValueSource; +import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMapImpl; import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap; @@ -38,6 +41,9 @@ import ca.bc.gov.nrs.vdyp.model.VdypSpecies; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestLayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; class VdypMatchersTest { @@ -169,7 +175,8 @@ void testAgeDifferent() { actual.ageTotals[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"ageTotals\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <40.0>\\] field \\\"ageTotals\\\" item 1: was a java.lang.Float \\(<41.0F>\\)" ) ); @@ -181,7 +188,8 @@ void testDomHeightDifferent() { actual.dominantHeights[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"dominantHeights\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <15.0>\\] field \\\"dominantHeights\\\" item 1: was a java.lang.Float \\(<16.0F>\\)" ) ); @@ -193,7 +201,8 @@ void testPercentForestDifferent() { actual.percentagesOfForestedLand[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"percentagesOfForestedLand\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <70.0>\\] field \\\"percentagesOfForestedLand\\\" item 1: was a java.lang.Float \\(<71.0F>\\)" ) ); @@ -205,7 +214,8 @@ void testSIDifferent() { actual.siteIndices[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"siteIndices\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <4.0>\\] field \\\"siteIndices\\\" item 1: was a java.lang.Float \\(<5.0F>\\)" ) ); @@ -217,7 +227,8 @@ void testYearsAtBHDifferent() { actual.yearsAtBreastHeight[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"yearsAtBreastHeight\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <35.0>\\] field \\\"yearsAtBreastHeight\\\" item 1: was a java.lang.Float \\(<36.0F>\\)" ) ); @@ -229,7 +240,8 @@ void testYearsToBHDifferent() { actual.yearsToBreastHeight[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"yearsToBreastHeight\\\" that is \\[a numeric value within <[^>]+> of <0.0>, a numeric value within <[^>]+> of <5.0>\\] field \\\"yearsToBreastHeight\\\" item 1: was a java.lang.Float \\(<6.0F>\\)" ) ); @@ -241,7 +253,8 @@ void testSCNDifferent() { actual.siteCurveNumbers[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"siteCurveNumbers\\\" that is \\[<0>, <42>\\] field \\\"siteCurveNumbers\\\" item 1: was <43>" ) ); @@ -253,7 +266,8 @@ void testSpecIndexDifferent() { actual.speciesIndices[1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \\\"speciesIndices\\\" that is \\[<0>, <10>\\] field \\\"speciesIndices\\\" item 1: was <11>" ) ); @@ -265,7 +279,8 @@ void testSpecNameDifferent() { actual.speciesNames[1] = "X"; assertMismatch( - actual, unit, equalTo( + actual, unit, + equalTo( "has field named \"speciesNames\" that is [null, \"MB\"] field \"speciesNames\" item 1: was \"X\"" ) ); @@ -273,14 +288,8 @@ actual, unit, equalTo( @ParameterizedTest @ValueSource( - strings = { - "loreyHeights", - "basalAreas", - "closeUtilizationVolumes", - "cuVolumesMinusDecay", - "cuVolumesMinusDecayAndWastage", - "quadMeanDiameters", - "treesPerHectare", + strings = { "loreyHeights", "basalAreas", "closeUtilizationVolumes", "cuVolumesMinusDecay", + "cuVolumesMinusDecayAndWastage", "quadMeanDiameters", "treesPerHectare", "wholeStemVolumes" } ) void testUtilizationDifferent(String name) throws Exception { @@ -290,7 +299,8 @@ void testUtilizationDifferent(String name) throws Exception { ((float[][]) field.get(actual))[1][1] += 1; assertMismatch( - actual, unit, matchesRegex( + actual, unit, + matchesRegex( "has field named \"" + name + "\" that is .+? field \"" + name + "\" item 1: item 1: was a java\\.lang\\.Float \\(<\\d+\\.\\d+F>\\)" ) @@ -1248,6 +1258,214 @@ void testSiteIndexDifferent() { } } + + @Nested + class testLayerProcessingState { + VdypPolygon expectedPolygon; + TestProcessingState expectedState; + TestLayerProcessingState expectedLayerState; + Matcher unit; + Map controlMap; + + @BeforeEach + void setup() throws ProcessingException { + controlMap = TestUtils.loadControlMap(); + expectedPolygon = VdypPolygon.build(pb -> { + + pb.polygonIdentifier("Test", 2024); + pb.percentAvailable(90f); + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.addLayer(lb -> { + + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + }); + + expectedState = new TestProcessingState(controlMap); + expectedState.setPolygon(expectedPolygon); + expectedLayerState = expectedState.getPrimaryLayerProcessingState(); + + unit = VdypMatchers.deepEquals(expectedLayerState); + } + + @Test + void testPass() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + var actualLayerState = actualState.getPrimaryLayerProcessingState(); + + assertMatch(actualLayerState, unit); + } + + @Test + void testDifferentPoly() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.polygonIdentifier("Different", 2025); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + var actualLayerState = actualState.getPrimaryLayerProcessingState(); + + assertMismatch( + actualLayerState, unit, + equalTo( + "hasProperty(\"polygon\", hasProperty(\"polygonIdentifier\", (an instance of ca.bc.gov.nrs.vdyp.model.PolygonIdentifier and hasProperty(\"base\", is \"Test\") and hasProperty(\"year\", is <2024>)))) property 'polygon' property 'polygonIdentifier' hasProperty(\"base\", is \"Test\") property 'base' was \"Different\"" + ) + ); + } + + @Test + void testDifferentLayer() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.layerType(LayerType.VETERAN); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + var actualLayerState = actualState.getVeteranLayerProcessingState().get(); + + assertMismatch( + actualLayerState, unit, + equalTo("hasProperty(\"layerType\", ) property 'layerType' was ") + ); + } + + @Test + void testDifferentBank() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + var actualLayerState = actualState.getPrimaryLayerProcessingState(); + + actualLayerState.getBank().basalAreas[1][1] += 1; + + assertMismatch( + actualLayerState, unit, + Matchers.matchesRegex( + ".*?field \\\"basalAreas\\\" item 1: item 1: was a java\\.lang\\.Float \\(<\\d+\\.\\d+F>\\).*" + ) + ); + } + } } @Nested From 46e1968d72d8eb84b546eb4a0e334b74d5d1c2fe Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Thu, 28 Nov 2024 12:10:32 -0800 Subject: [PATCH 41/45] Deep equals for processing state --- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 22 ++ .../gov/nrs/vdyp/test/VdypMatchersTest.java | 224 ++++++++++++++++++ 2 files changed, 246 insertions(+) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 9b07a761d..d452fa6b3 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -64,6 +64,7 @@ import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; /** * Custom Hamcrest Matchers @@ -239,6 +240,12 @@ public void describeMismatch(Object item, Description description) { }; } + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Matcher> + optional(Optional expected, Function> valueMatcherFactory) { + return expected.map(v -> present((Matcher) valueMatcherFactory.apply(v))).orElse(notPresent()); + } + public static Matcher> mmHasEntry(Matcher valueMatcher, Object... keys) { return new BaseMatcher>() { @@ -1395,4 +1402,19 @@ public static Matcher deepEquals(Bank expected) { return allOf(matchers); } + + public static Matcher deepEquals(TestProcessingState expectedState) { + return allOf( + hasProperty( + "currentPolygon", + hasProperty( + "polygonIdentifier", isPolyId(expectedState.getCurrentPolygon().getPolygonIdentifier()) + ) + ), + hasProperty( + "veteranLayerProcessingState", + optional(expectedState.getVeteranLayerProcessingState(), v -> deepEquals(v)) + ) + ); + } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java index 0b529811f..fd802276c 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchersTest.java @@ -1466,6 +1466,230 @@ void testDifferentBank() throws ProcessingException { ); } } + + @Nested + class testProcessingState { + VdypPolygon expectedPolygon; + TestProcessingState expectedState; + Matcher unit; + Map controlMap; + + @BeforeEach + void setup() throws ProcessingException { + controlMap = TestUtils.loadControlMap(); + expectedPolygon = VdypPolygon.build(pb -> { + + pb.polygonIdentifier("Test", 2024); + pb.percentAvailable(90f); + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.addLayer(lb -> { + + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + }); + + expectedState = new TestProcessingState(controlMap); + expectedState.setPolygon(expectedPolygon); + + unit = VdypMatchers.deepEquals(expectedState); + } + + @Test + void testPass() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + + assertMatch(actualState, unit); + } + + @Test + void testPolyDifferent() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + pb.polygonIdentifier("Different", 2025); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + assertMismatch( + actualState, unit, + equalTo( + "hasProperty(\"currentPolygon\", hasProperty(\"polygonIdentifier\", (an instance of ca.bc.gov.nrs.vdyp.model.PolygonIdentifier and hasProperty(\"base\", is \"Test\") and hasProperty(\"year\", is <2024>)))) property 'currentPolygon' property 'polygonIdentifier' hasProperty(\"base\", is \"Test\") property 'base' was \"Different\"" + ) + ); + } + + @Test + void testUnexpectedVeteranLayer() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.layerType(LayerType.VETERAN); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + actualState.setPolygon(actualPolygon); + assertMismatch( + actualState, unit, + startsWith( + "hasProperty(\"veteranLayerProcessingState\", Optional that is empty) property 'veteranLayerProcessingState' had value" + ) + ); + } + + @Test + void testMissingVeteranLayer() throws ProcessingException { + var actualState = new TestProcessingState(controlMap); + + var actualPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + expectedPolygon = VdypPolygon.build(pb -> { + pb.copy(expectedPolygon); + + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + pb.copyLayers(expectedPolygon, (lb, expectedLayer) -> { + lb.copy(expectedLayer); + lb.layerType(LayerType.VETERAN); + lb.copySpecies(expectedLayer, (sb, expectedSpec) -> { + sb.copy(expectedSpec); + sb.copySiteFrom(expectedSpec, (ib, i) -> { + }); + sb.copyCompatibilityVariablesFrom(expectedSpec, (ib, cv) -> { + }); + }); + }); + }); + expectedState.setPolygon(expectedPolygon); + unit = VdypMatchers.deepEquals(expectedState); + + actualState.setPolygon(actualPolygon); + assertMismatch( + actualState, unit, Matchers.endsWith("property 'veteranLayerProcessingState' Not present") + ); + } + } } @Nested From 3e972eb21dd4c037dc478fee37425b7230967c93 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 3 Dec 2024 02:35:30 -0800 Subject: [PATCH 42/45] State dump Bank. --- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 544 +++++++++++------- .../bc/gov/nrs/vdyp/test/TestUtilsTest.java | 152 ++++- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 7 +- 3 files changed, 459 insertions(+), 244 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index db2e32332..55f7838f7 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -25,12 +25,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Random; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.hamcrest.Matcher; import org.hamcrest.Matchers; @@ -67,7 +69,9 @@ import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; public class TestUtils { @@ -645,8 +649,13 @@ public static BecDefinition mockBec() { return new BecDefinition("T", Region.COASTAL, "Test"); } - private static void line(Appendable out, String line, Object... args) throws UncheckedIOException { + private static void line(Appendable out, int indent, String line, Object... args) throws UncheckedIOException { try { + if (!line.isEmpty()) { + for (int i = 0; i < indent; i++) { + out.append("\t"); + } + } out.append(String.format(line, args)).append("\n"); } catch (IOException e) { new UncheckedIOException(e); @@ -654,6 +663,9 @@ private static void line(Appendable out, String line, Object... args) throws Unc } private static String stringLiteral(String s) { + if (s == null) { + return "null"; + } return String.format("\"%s\"", StringEscapeUtils.escapeJava(s)); } @@ -698,255 +710,349 @@ private static String optionalLiteral(Optional opt, Function v return opt.map(valueLiteral).map(s -> String.format("Optional.of(%s)", s)).orElse("Optional.empty()"); } - /** - * Serializes a VdypPolygon as Java code that can be executed to recreate it. Meant to be used to aid in creating - * unit tests. - */ - public static void writeModel(VdypPolygon poly, Appendable out, String assignTo) throws IOException { - try { - line(out, "/* the following Polygon definition was generated */"); - line(out, ""); - line(out, "%s = VdypPolygon.build(pb -> {", assignTo); + private static void writeBuilderConfig(VdypLayer layer, Appendable out, int indent, String assignTo) + throws IOException { + line(out, indent, "lb.layerType(%s);", enumLiteral(layer.getLayerType())); + line(out, indent, ""); + line( + out, indent, "lb.empiricalRelationshipParameterIndex(%s);", + optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), TestUtils::intLiteral) + ); + line(out, indent, ""); + line( + out, indent, " lb.inventoryTypeGroup(%s);", + optionalLiteral(layer.getInventoryTypeGroup(), TestUtils::intLiteral) + ); + line(out, indent, ""); + line(out, indent, "lb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); + line(out, indent, "lb.treesPerHectare(%s);", utilVectorLiteral(layer.getTreesPerHectareByUtilization())); + line( + out, indent, " lb.quadMeanDiameter(%s);", + utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) + ); + line(out, indent, "lb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); + line(out, indent, ""); + line(out, indent, "lb.wholeStemVolume(%s);", utilVectorLiteral(layer.getWholeStemVolumeByUtilization())); + line( + out, indent, "lb.closeUtilizationVolumeByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + ); + line( + out, indent, "lb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) + ); + line( + out, indent, "lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) + ); + line( + out, indent, "lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + ); + line(out, indent, ""); + for (var spec : layer.getSpecies().values()) { + line(out, indent, "lb.addSpecies(sb -> {"); + line(out, indent, " sb.genus(%s);", stringLiteral(spec.getGenus())); + line(out, indent, " sb.genus(%d);", spec.getGenusIndex()); + line(out, indent, ""); + line(out, indent, " sb.breakageGroup(%d);", spec.getBreakageGroup()); + line(out, indent, " sb.volumeGroup(%d);", spec.getVolumeGroup()); + line(out, indent, " sb.decayGroup(%d);", spec.getDecayGroup()); + line(out, indent, ""); + line(out, indent, " sb.percentGenus(%s);", floatLiteral(spec.getPercentGenus())); + line(out, indent, ""); + + spec.getSp64DistributionSet().getSp64DistributionList().forEach(sp64 -> { + line( + out, indent, " sb.addSp64Distribution(%s, %s);", stringLiteral(sp64.getGenusAlias()), + floatLiteral(sp64.getPercentage()) + ); + }); + line(out, indent, ""); + line(out, indent, " sb.loreyHeight(%s);", utilVectorLiteral(spec.getLoreyHeightByUtilization())); + line(out, indent, " sb.treesPerHectare(%s);", utilVectorLiteral(spec.getTreesPerHectareByUtilization())); line( - out, " pb.polygonIdentifier(%s, %d);", stringLiteral(poly.getPolygonIdentifier().getBase()), - poly.getPolygonIdentifier().getYear() + out, indent, " sb.quadMeanDiameter(%s);", + utilVectorLiteral(spec.getQuadraticMeanDiameterByUtilization()) ); - - line(out, ""); + line(out, indent, " sb.baseArea(%s);", utilVectorLiteral(spec.getBaseAreaByUtilization())); + line(out, indent, ""); + line(out, indent, " sb.wholeStemVolume(%s);", utilVectorLiteral(spec.getWholeStemVolumeByUtilization())); line( - out, " pb.biogeoclimaticZone(Utils.getBec(%s, controlMap));", - stringLiteral(poly.getBiogeoclimaticZone().getAlias()) + out, indent, " sb.closeUtilizationVolumeByUtilization(%s);", + utilVectorLiteral(spec.getCloseUtilizationVolumeByUtilization()) ); - line(out, " pb.forestInventoryZone(%s);", stringLiteral(poly.getForestInventoryZone())); - line(out, ""); line( - out, " pb.inventoryTypeGroup(%s);", - optionalLiteral(poly.getInventoryTypeGroup(), TestUtils::intLiteral) + out, indent, " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayByUtilization()) ); - line(out, " pb.targetYear(%s);", optionalLiteral(poly.getTargetYear(), TestUtils::intLiteral)); - line(out, ""); - line(out, " pb.mode(%s);", optionalLiteral(poly.getMode(), TestUtils::enumLiteral)); - line(out, " pb.percentAvailable(%s);", floatLiteral(poly.getPercentAvailable())); - line(out, ""); - for (var layer : poly.getLayers().values()) { - line(out, " pb.addLayer(lb -> {"); - line(out, " lb.layerType(%s);", enumLiteral(layer.getLayerType())); - line(out, ""); + line( + out, indent, " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) + ); + line( + out, indent, " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", + utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + ); + line(out, indent, ""); + spec.getCompatibilityVariables().ifPresent(cv -> { + line(out, indent, " sb.addCompatibilityVariables(cvb -> {"); + line(out, indent, ""); + line( - out, " lb.empiricalRelationshipParameterIndex(%s);", - optionalLiteral(layer.getEmpiricalRelationshipParameterIndex(), TestUtils::intLiteral) + out, indent, + " MatrixMap3 cvVolume = new MatrixMap3Impl<>(" ); - line(out, ""); + line(out, indent, " UtilizationClass.UTIL_CLASSES, "); + line(out, indent, " VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, "); + line(out, indent, " LayerType.ALL_USED,"); + line(out, indent, " (uc, vv, lt) -> 0f"); + line(out, indent, " );"); + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { + for (var lt : LayerType.ALL_USED) { + line( + out, indent, + " cvVolume.put(UtilizationClass.%s, UtilizationClassVariable.%s, LayerType.%s, %s);", + uc, vv, lt, floatLiteral(cv.getCvVolume(uc, vv, lt)) + ); + } + } + } line( - out, " lb.inventoryTypeGroup(%s);", - optionalLiteral(layer.getInventoryTypeGroup(), TestUtils::intLiteral) + out, indent, + " MatrixMap2 cvBasalArea = new MatrixMap2Impl<>(" ); - line(out, ""); - line(out, " lb.loreyHeight(%s);", utilVectorLiteral(layer.getLoreyHeightByUtilization())); - line(out, " lb.treesPerHectare(%s);", utilVectorLiteral(layer.getTreesPerHectareByUtilization())); + line(out, indent, " UtilizationClass.UTIL_CLASSES, "); + line(out, indent, " LayerType.ALL_USED,"); + line(out, indent, " (uc, lt) -> 0f"); + line(out, indent, " );"); + + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + line( + out, indent, " cvBasalArea.put(UtilizationClass.%s, LayerType.%s, %s);", uc, lt, + floatLiteral(cv.getCvBasalArea(uc, lt)) + ); + } + } + line( - out, " lb.quadMeanDiameter(%s);", - utilVectorLiteral(layer.getQuadraticMeanDiameterByUtilization()) + out, indent, + " MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>(" ); - line(out, " lb.baseArea(%s);", utilVectorLiteral(layer.getBaseAreaByUtilization())); - line(out, ""); - line(out, " lb.wholeStemVolume(%s);", utilVectorLiteral(layer.getWholeStemVolumeByUtilization())); + line(out, indent, " UtilizationClass.UTIL_CLASSES, "); + line(out, indent, " LayerType.ALL_USED,"); + line(out, indent, " (uc, lt) -> 0f"); + line(out, indent, " );"); + line(out, indent, ""); + for (var uc : UtilizationClass.UTIL_CLASSES) { + for (var lt : LayerType.ALL_USED) { + line( + out, indent, + " cvQuadraticMeanDiameter.put(UtilizationClass.%s, LayerType.%s, %s);", uc, lt, + floatLiteral(cv.getCvQuadraticMeanDiameter(uc, lt)) + ); + } + } + line(out, indent, ""); + line(out, indent, " Map cvPrimaryLayerSmall = new HashMap<>();"); + line(out, indent, ""); + for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { + line( + out, indent, " cvPrimaryLayerSmall.put(UtilizationClassVariable.%s, %s);", ucv, + floatLiteral(cv.getCvPrimaryLayerSmall(ucv)) + ); + } + line(out, indent, ""); + line(out, indent, " cvb.cvVolume(cvVolume);"); + line(out, indent, " cvb.cvBasalArea(cvBasalArea);"); + line(out, indent, " cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter);"); + line(out, indent, " cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall);"); + line(out, indent, " });"); + }); + line(out, indent, ""); + spec.getSite().ifPresent(site -> { + line(out, indent, ""); + + line(out, indent, " sb.addSite(ib -> {"); line( - out, " lb.closeUtilizationVolumeByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeByUtilization()) + out, indent, " ib.ageTotal(%s);", + optionalLiteral(site.getAgeTotal(), TestUtils::floatLiteral) ); + line(out, indent, " ib.height(%s);", optionalLiteral(site.getHeight(), TestUtils::floatLiteral)); line( - out, " lb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayByUtilization()) + out, indent, " ib.siteCurveNumber(%s);", + optionalLiteral(site.getSiteCurveNumber(), TestUtils::intLiteral) ); line( - out, " lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) + out, indent, " ib.siteIndex(%s);", + optionalLiteral(site.getSiteIndex(), TestUtils::floatLiteral) ); line( - out, " lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", - utilVectorLiteral(layer.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) + out, indent, " ib.yearsToBreastHeight(%s);", + optionalLiteral(site.getYearsToBreastHeight(), TestUtils::floatLiteral) ); - line(out, ""); - for (var spec : layer.getSpecies().values()) { - line(out, " lb.addSpecies(sb -> {"); - line(out, " sb.genus(%s);", stringLiteral(spec.getGenus())); - line(out, " sb.genus(%d);", spec.getGenusIndex()); - line(out, ""); - line(out, " sb.breakageGroup(%d);", spec.getBreakageGroup()); - line(out, " sb.volumeGroup(%d);", spec.getVolumeGroup()); - line(out, " sb.decayGroup(%d);", spec.getDecayGroup()); - line(out, ""); - line(out, " sb.percentGenus(%s);", floatLiteral(spec.getPercentGenus())); - line(out, ""); - - spec.getSp64DistributionSet().getSp64DistributionList().forEach(sp64 -> { - line( - out, " sb.addSp64Distribution(%s, %s);", stringLiteral(sp64.getGenusAlias()), - floatLiteral(sp64.getPercentage()) - ); - }); + line(out, indent, " });"); + line(out, indent, ""); + }); + line(out, indent, ""); + line(out, indent, "});"); + } + line(out, indent, ""); + line(out, indent, "lb.primaryGenus(%s);", optionalLiteral(layer.getPrimaryGenus(), TestUtils::stringLiteral)); - line(out, ""); - line(out, " sb.loreyHeight(%s);", utilVectorLiteral(spec.getLoreyHeightByUtilization())); - line( - out, " sb.treesPerHectare(%s);", - utilVectorLiteral(spec.getTreesPerHectareByUtilization()) - ); - line( - out, " sb.quadMeanDiameter(%s);", - utilVectorLiteral(spec.getQuadraticMeanDiameterByUtilization()) - ); - line(out, " sb.baseArea(%s);", utilVectorLiteral(spec.getBaseAreaByUtilization())); - line(out, ""); - line( - out, " sb.wholeStemVolume(%s);", - utilVectorLiteral(spec.getWholeStemVolumeByUtilization()) - ); - line( - out, " sb.closeUtilizationVolumeByUtilization(%s);", - utilVectorLiteral(spec.getCloseUtilizationVolumeByUtilization()) - ); - line( - out, " sb.closeUtilizationVolumeNetOfDecayByUtilization(%s);", - utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayByUtilization()) - ); - line( - out, " sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(%s);", - utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization()) - ); - line( - out, " sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(%s);", - utilVectorLiteral(spec.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization()) - ); - line(out, ""); - spec.getCompatibilityVariables().ifPresent(cv -> { - line(out, " sb.addCompatibilityVariables(cvb -> {"); - line(out, ""); + } - line( - out, - " MatrixMap3 cvVolume = new MatrixMap3Impl<>(" - ); - line(out, " UtilizationClass.UTIL_CLASSES, "); - line(out, " VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, "); - line(out, " LayerType.ALL_USED,"); - line(out, " (uc, vv, lt) -> 0f"); - line(out, " );"); - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var vv : VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES) { - for (var lt : LayerType.ALL_USED) { - line( - out, - " cvVolume.put(UtilizationClass.%s, UtilizationClassVariable.%s, LayerType.%s, %s);", - uc, vv, lt, floatLiteral(cv.getCvVolume(uc, vv, lt)) - ); - } - } - } - line( - out, - " MatrixMap2 cvBasalArea = new MatrixMap2Impl<>(" - ); - line(out, " UtilizationClass.UTIL_CLASSES, "); - line(out, " LayerType.ALL_USED,"); - line(out, " (uc, lt) -> 0f"); - line(out, " );"); - - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var lt : LayerType.ALL_USED) { - line( - out, " cvBasalArea.put(UtilizationClass.%s, LayerType.%s, %s);", - uc, lt, floatLiteral(cv.getCvBasalArea(uc, lt)) - ); - } - } + /** + * Serializes a VdypPolygon as Java code that can be executed to recreate it. Meant to be used to aid in creating + * unit tests. + */ + public static void writeModel(VdypPolygon poly, Appendable out, int indent, String assignTo) throws IOException { + try { + line(out, indent, "/* the following Polygon definition was generated */"); + line(out, indent, ""); + line(out, indent, "%s = VdypPolygon.build(pb -> {", assignTo); - line( - out, - " MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>(" - ); - line(out, " UtilizationClass.UTIL_CLASSES, "); - line(out, " LayerType.ALL_USED,"); - line(out, " (uc, lt) -> 0f"); - line(out, " );"); - line(out, ""); - for (var uc : UtilizationClass.UTIL_CLASSES) { - for (var lt : LayerType.ALL_USED) { - line( - out, - " cvQuadraticMeanDiameter.put(UtilizationClass.%s, LayerType.%s, %s);", - uc, lt, floatLiteral(cv.getCvQuadraticMeanDiameter(uc, lt)) - ); - } - } - line(out, ""); - line( - out, - " Map cvPrimaryLayerSmall = new HashMap<>();" - ); - line(out, ""); - for (var ucv : VdypCompatibilityVariables.SMALL_UTILIZATION_VARIABLES) { - line( - out, " cvPrimaryLayerSmall.put(UtilizationClassVariable.%s, %s);", ucv, - floatLiteral(cv.getCvPrimaryLayerSmall(ucv)) - ); - } - line(out, ""); - line(out, " cvb.cvVolume(cvVolume);"); - line(out, " cvb.cvBasalArea(cvBasalArea);"); - line(out, " cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter);"); - line(out, " cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall);"); - line(out, " });"); - }); - line(out, ""); - spec.getSite().ifPresent(site -> { - line(out, ""); - - line(out, " sb.addSite(ib -> {"); - line( - out, " ib.ageTotal(%s);", - optionalLiteral(site.getAgeTotal(), TestUtils::floatLiteral) - ); - line( - out, " ib.height(%s);", - optionalLiteral(site.getHeight(), TestUtils::floatLiteral) - ); - line( - out, " ib.siteCurveNumber(%s);", - optionalLiteral(site.getSiteCurveNumber(), TestUtils::intLiteral) - ); - line( - out, " ib.siteIndex(%s);", - optionalLiteral(site.getSiteIndex(), TestUtils::floatLiteral) - ); - line( - out, " ib.yearsToBreastHeight(%s);", - optionalLiteral(site.getYearsToBreastHeight(), TestUtils::floatLiteral) - ); - line(out, " });"); - line(out, ""); - }); - line(out, ""); - line(out, " });"); - } - line(out, ""); - line( - out, " lb.primaryGenus(%s);", - optionalLiteral(layer.getPrimaryGenus(), TestUtils::stringLiteral) - ); - line(out, " });"); + line( + out, indent, " pb.polygonIdentifier(%s, %d);", + stringLiteral(poly.getPolygonIdentifier().getBase()), poly.getPolygonIdentifier().getYear() + ); + + line(out, indent, ""); + line( + out, indent, " pb.biogeoclimaticZone(Utils.getBec(%s, controlMap));", + stringLiteral(poly.getBiogeoclimaticZone().getAlias()) + ); + line(out, indent, " pb.forestInventoryZone(%s);", stringLiteral(poly.getForestInventoryZone())); + line(out, indent, ""); + line( + out, indent, " pb.inventoryTypeGroup(%s);", + optionalLiteral(poly.getInventoryTypeGroup(), TestUtils::intLiteral) + ); + line(out, indent, " pb.targetYear(%s);", optionalLiteral(poly.getTargetYear(), TestUtils::intLiteral)); + line(out, indent, ""); + line(out, indent, " pb.mode(%s);", optionalLiteral(poly.getMode(), TestUtils::enumLiteral)); + line(out, indent, " pb.percentAvailable(%s);", floatLiteral(poly.getPercentAvailable())); + line(out, indent, ""); + for (var layer : poly.getLayers().values()) { + line(out, indent, " pb.addLayer(lb -> {"); + writeBuilderConfig(layer, out, indent + 2, assignTo); + line(out, indent, " });"); } - line(out, "});"); + line(out, indent, "});"); - line(out, ""); + line(out, indent, ""); - line(out, "/* End of generated polygon Definition */"); + line(out, indent, "/* End of generated Polygon definition */"); } catch (UncheckedIOException e) { throw new IOException(e); } } + static String arrayLiteral(String[] arr) { + return Arrays.stream(arr).map(TestUtils::stringLiteral).collect(Collectors.joining(", ", "new String[]{", "}")); + } + + static String arrayLiteral(int[] arr) { + return Arrays.stream(ArrayUtils.toObject(arr)).map(i -> Integer.toString(i)) + .collect(Collectors.joining(", ", "new int[]{", "}")); + } + + static String arrayLiteral(float[] arr) { + return Arrays.stream(ArrayUtils.toObject(arr)).map(TestUtils::floatLiteral) + .collect(Collectors.joining(", ", "new float[]{", "}")); + } + + static String arrayLiteral(float[][] arr) { + return Arrays.stream(arr) + .map( + subArr -> Arrays.stream(ArrayUtils.toObject(subArr)).map(TestUtils::floatLiteral) + .collect(Collectors.joining(", ", "{", "}")) + ).collect(Collectors.joining(", ", "new float[][]{", "}")); + } + + static void jitterArray(int[] arr, Random rand) { + for (int i = 0; i < arr.length; i++) { + arr[i] += rand.nextInt(1) * 2 - 1; + } + } + + static void jitterArray(float[] arr, Random rand) { + for (int i = 0; i < arr.length; i++) { + arr[i] *= rand.nextFloat(0.9f, 1.1f); + } + } + + static void jitterArray(float[][] arr, Random rand) { + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr[i].length; j++) { + arr[i][j] *= rand.nextFloat(0.9f, 1.1f); + } + } + } + + public static void writeModel(Bank expected, Appendable out, int indent, String assignTo, String layerVar) { + + line(out, indent, "/* the following Bank definition was generated */"); + line(out, indent, ""); + + line( + out, indent, "var validSpecGroupIndices = %s;", + Arrays.stream(expected.speciesIndices).mapToObj((int i) -> Integer.toString(i)) + .collect(Collectors.joining(", ", "List.of(", ")")) + ); + + line( + out, indent, + "%s = new Bank(%s, Utils.getBec(%s, controlMap), spec->validSpecGroupIndices.contains(spec.getGenusIndex()));", + assignTo, layerVar, stringLiteral(expected.getBecZone().getAlias()) + ); + + // System.arraycopy(src, srcPos, dest, destPos, len); + line( + out, indent, "System.arraycopy(%s, 0, %s.ageTotals, 0, %d);", arrayLiteral(expected.ageTotals), + assignTo, expected.ageTotals.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.percentagesOfForestedLand, 0, %d);", + arrayLiteral(expected.percentagesOfForestedLand), assignTo, expected.percentagesOfForestedLand.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.basalAreas, 0, %d);", arrayLiteral(expected.basalAreas), + assignTo, expected.basalAreas.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.speciesNames, 0, %d);", arrayLiteral(expected.speciesNames), + assignTo, expected.speciesNames.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.siteIndices, 0, %d);", arrayLiteral(expected.siteIndices), + assignTo, expected.siteIndices.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.dominantHeights, 0, %d);", + arrayLiteral(expected.dominantHeights), assignTo, expected.dominantHeights.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.yearsAtBreastHeight, 0, %d);", + arrayLiteral(expected.yearsAtBreastHeight), assignTo, expected.yearsAtBreastHeight.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.yearsToBreastHeight, 0, %d);", + arrayLiteral(expected.yearsToBreastHeight), assignTo, expected.yearsToBreastHeight.length + ); + line( + out, indent, "System.arraycopy(%s, 0, %s.siteCurveNumbers, 0, %d);", + arrayLiteral(expected.siteCurveNumbers), assignTo, expected.siteCurveNumbers.length + ); + + line(out, indent, ""); + + line(out, indent, "/* End of generated Bank definition */"); + + } + } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java index 575951c07..792c1cc4c 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Random; @@ -22,30 +23,146 @@ import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.UtilizationVector; import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; public class TestUtilsTest { + Random rand; + + @BeforeEach + void setup() { + rand = new Random(42); + } + + UtilizationVector mockUtilVector(float multiplier) { + return Utils.utilizationVector( + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, + rand.nextFloat() * multiplier, rand.nextFloat() * multiplier + ); + } + + UtilizationVector mockHeightVector() { + return Utils.heightVector(rand.nextFloat() * 5, rand.nextFloat() * 20); + } @Nested - class WriteModel { - Random rand; + class WriteProcessingState { + @Test + void testBank() throws IOException { + var controlMap = TestUtils.loadControlMap(); + var expectedBec = Utils.getBec("CDF", controlMap); + var expectedLayer = VdypLayer.build(lb -> { + lb.polygonIdentifier("Test", 2024); + lb.layerType(LayerType.PRIMARY); - @BeforeEach - void setup() { - rand = new Random(42); - } + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); - UtilizationVector mockUtilVector(float multiplier) { - return Utils.utilizationVector( - rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, rand.nextFloat() * multiplier, - rand.nextFloat() * multiplier, rand.nextFloat() * multiplier + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + Bank expected = new Bank(expectedLayer, expectedBec, x -> true); + + // Change the bank values from those initially set to match the layer + TestUtils.jitterArray(expected.ageTotals, rand); + TestUtils.jitterArray(expected.basalAreas, rand); + TestUtils.jitterArray(expected.dominantHeights, rand); + TestUtils.jitterArray(expected.siteCurveNumbers, rand); + TestUtils.jitterArray(expected.siteIndices, rand); + TestUtils.jitterArray(expected.yearsAtBreastHeight, rand); + TestUtils.jitterArray(expected.yearsToBreastHeight, rand); + + expected.percentagesOfForestedLand[1] = 70; + + var buf = new StringBuffer(); + TestUtils.writeModel(expected, buf, 3, "result", "expectedLayer"); + + System.out.print(buf.toString()); + + Bank result = null; + + /* the following Bank definition was generated */ + + var validSpecGroupIndices = List.of(0, 10); + result = new Bank( + expectedLayer, Utils.getBec("CDF", controlMap), + spec -> validSpecGroupIndices.contains(spec.getGenusIndex()) ); - } + System.arraycopy(new float[] { 0.000000f, 41.552448f }, 0, result.ageTotals, 0, 2); + System.arraycopy(new float[] { 0.000000f, 70.000000f }, 0, result.percentagesOfForestedLand, 0, 2); + System.arraycopy( + new float[][] { { 1.073338f, 3.073815f, 0.181232f, 1.592524f, 1.446236f, 0.064980f }, + { 1.052509f, 3.325452f, 0.180743f, 1.492788f, 1.646765f, 0.059891f } }, + 0, result.basalAreas, 0, 2 + ); + System.arraycopy(new String[] { null, "MB" }, 0, result.speciesNames, 0, 2); + System.arraycopy(new float[] { 0.000000f, 4.253897f }, 0, result.siteIndices, 0, 2); + System.arraycopy(new float[] { 0.000000f, 14.915393f }, 0, result.dominantHeights, 0, 2); + System.arraycopy(new float[] { 0.000000f, 38.343075f }, 0, result.yearsAtBreastHeight, 0, 2); + System.arraycopy(new float[] { 0.000000f, 5.202045f }, 0, result.yearsToBreastHeight, 0, 2); + System.arraycopy(new int[] { -1, 41 }, 0, result.siteCurveNumbers, 0, 2); + + /* End of generated Bank definition */ - UtilizationVector mockHeightVector() { - return Utils.heightVector(rand.nextFloat() * 5, rand.nextFloat() * 20); + assertThat(result, deepEquals(expected)); } + } + + @Nested + class WriteModel { @Test void polygon() throws IOException { @@ -122,7 +239,7 @@ void polygon() throws IOException { }); var buf = new StringBuffer(); - TestUtils.writeModel(poly, buf, "result"); + TestUtils.writeModel(poly, buf, 3, "result"); System.out.print(buf.toString()); @@ -134,13 +251,6 @@ void polygon() throws IOException { private VdypPolygon get(Map controlMap) { VdypPolygon result = null; - /* - * There is probably a way to automate this but it would require some additional work in Maven. - * - * For now, if something changes how the code generation works, Run the test, copy the output to Stdout to - * replace the code delimited by the following comments and run again to confirm the test passes. - */ - /* the following Polygon definition was generated */ result = VdypPolygon.build(pb -> { diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index d452fa6b3..a7c9a8dc3 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.test; +import static ca.bc.gov.nrs.vdyp.test.TestUtils.UTIL_CLASSES; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.anything; @@ -1386,10 +1387,8 @@ public static Matcher deepEquals(Bank expected) { ).forEach(matchers::add); - Stream.of("speciesNames", "sp64Distributions") - .map(name -> hasField(name, Matchers.arrayContaining((Object[]) getField(name, expected)))) - .forEach(matchers::add); - + matchers.add(hasField("speciesNames", arrayContaining(expected.speciesNames))); + matchers.add(hasField("sp64Distributions", arrayContaining(expected.sp64Distributions))); Stream.of( "siteIndices", "dominantHeights", "ageTotals", "yearsAtBreastHeight", "yearsToBreastHeight", "percentagesOfForestedLand" From 72906eaea4a07687eddbfe21a503e989c008bd88 Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 3 Dec 2024 20:06:42 -0800 Subject: [PATCH 43/45] ProcessingState write state for tests --- .../ca/bc/gov/nrs/vdyp/test/TestUtils.java | 49 +- .../bc/gov/nrs/vdyp/test/TestUtilsTest.java | 805 +++++++++++++++++- .../ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 6 +- 3 files changed, 846 insertions(+), 14 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 55f7838f7..4eaddab36 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -72,6 +72,8 @@ import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; public class TestUtils { @@ -699,6 +701,12 @@ private static String enumLiteral(Enum e) { } private static String floatLiteral(float v) { + if (Float.isNaN(v)) { + return "Float.NaN"; + } + if (Float.isInfinite(v)) { + return v > 0 ? "Float.POSITIVE_INFINITY" : "Float.NEGATIVE_INFINITY"; + } return String.format("%ff", v); } @@ -1011,7 +1019,41 @@ public static void writeModel(Bank expected, Appendable out, int indent, String assignTo, layerVar, stringLiteral(expected.getBecZone().getAlias()) ); - // System.arraycopy(src, srcPos, dest, destPos, len); + writeBankConfig(expected, out, indent, assignTo); + + line(out, indent, ""); + + line(out, indent, "/* End of generated Bank definition */"); + + } + + public static void writeModel(ProcessingState expected, Appendable out, int indent, String assignTo) + throws IOException { + + line(out, indent, "/* the following ProcessingState definition was generated */"); + line(out, indent, ""); + + var poly = expected.getCurrentPolygon(); + + writeModel(poly, out, indent, "var polygon"); + + line(out, indent, "var state = new TestProcessingState(controlMap);"); + line(out, indent, "state.setPolygon(polygon);"); + line(out, indent, "var primaryBank = state.getPrimaryLayerProcessingState().getBank();"); + + writeBankConfig(expected.getPrimaryLayerProcessingState().getBank(), out, indent, "primaryBank"); + + expected.getVeteranLayerProcessingState().ifPresent(vetState -> { + line(out, indent, "var veteranBank = state.getVeteranLayerProcessingState().get().getBank();"); + writeBankConfig(vetState.getBank(), out, indent, "veteranBank"); + }); + + line(out, indent, ""); + line(out, indent, "/* End of generated ProcessingState definition */"); + + } + + public static void writeBankConfig(Bank expected, Appendable out, int indent, String assignTo) { line( out, indent, "System.arraycopy(%s, 0, %s.ageTotals, 0, %d);", arrayLiteral(expected.ageTotals), assignTo, expected.ageTotals.length @@ -1048,11 +1090,6 @@ assignTo, layerVar, stringLiteral(expected.getBecZone().getAlias()) out, indent, "System.arraycopy(%s, 0, %s.siteCurveNumbers, 0, %d);", arrayLiteral(expected.siteCurveNumbers), assignTo, expected.siteCurveNumbers.length ); - - line(out, indent, ""); - - line(out, indent, "/* End of generated Bank definition */"); - } } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java index 792c1cc4c..45f237c26 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; @@ -27,12 +28,15 @@ import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; public class TestUtilsTest { Random rand; @BeforeEach void setup() { + // Setting the seed to a fixed value before each test should make the WriteState tests repeatable. rand = new Random(42); } @@ -117,13 +121,7 @@ void testBank() throws IOException { Bank expected = new Bank(expectedLayer, expectedBec, x -> true); // Change the bank values from those initially set to match the layer - TestUtils.jitterArray(expected.ageTotals, rand); - TestUtils.jitterArray(expected.basalAreas, rand); - TestUtils.jitterArray(expected.dominantHeights, rand); - TestUtils.jitterArray(expected.siteCurveNumbers, rand); - TestUtils.jitterArray(expected.siteIndices, rand); - TestUtils.jitterArray(expected.yearsAtBreastHeight, rand); - TestUtils.jitterArray(expected.yearsToBreastHeight, rand); + jitterBank(expected); expected.percentagesOfForestedLand[1] = 70; @@ -159,6 +157,799 @@ void testBank() throws IOException { assertThat(result, deepEquals(expected)); } + + public void jitterBank(Bank expected) { + TestUtils.jitterArray(expected.ageTotals, rand); + TestUtils.jitterArray(expected.basalAreas, rand); + TestUtils.jitterArray(expected.dominantHeights, rand); + TestUtils.jitterArray(expected.siteCurveNumbers, rand); + TestUtils.jitterArray(expected.siteIndices, rand); + TestUtils.jitterArray(expected.yearsAtBreastHeight, rand); + TestUtils.jitterArray(expected.yearsToBreastHeight, rand); + } + + @Test + void testProcessingState() throws IOException, ProcessingException { + var controlMap = TestUtils.loadControlMap(); + + var expectedPolygon = VdypPolygon.build(pb -> { + + pb.polygonIdentifier("Test", 2024); + pb.percentAvailable(90f); + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.addLayer(lb -> { + + lb.layerType(LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("MB"); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("MB", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.addSite(ib -> { + ib.ageTotal(40); + ib.yearsToBreastHeight(5); + ib.height(15); + ib.siteCurveNumber(42); + ib.siteIndex(4); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + pb.addLayer(lb -> { + + lb.layerType(LayerType.VETERAN); + + lb.empiricalRelationshipParameterIndex(21); + lb.inventoryTypeGroup(34); + lb.primaryGenus("S"); + + lb.addSpecies(sb -> { + sb.genus("S"); + sb.controlMap(controlMap); + + sb.percentGenus(90); + + sb.breakageGroup(12); + sb.decayGroup(13); + sb.volumeGroup(14); + + sb.addSp64Distribution("S", 100); + + sb.addCompatibilityVariables(cvb -> { + cvb.cvVolume((k1, k2, k3) -> rand.nextFloat() * 10); + cvb.cvBasalArea((k1, k2) -> rand.nextFloat() * 10); + cvb.cvQuadraticMeanDiameter((k1, k2) -> rand.nextFloat() * 10); + cvb.cvPrimaryLayerSmall(k1 -> rand.nextFloat() * 10); + }); + + sb.loreyHeight(mockHeightVector()); + + sb.baseArea(mockUtilVector(2)); + sb.quadMeanDiameter(mockUtilVector(10)); + sb.treesPerHectare(mockUtilVector(300)); + + sb.wholeStemVolume(mockUtilVector(7)); + sb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + sb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + + lb.loreyHeight(mockHeightVector()); + + lb.baseArea(mockUtilVector(2)); + lb.quadMeanDiameter(mockUtilVector(10)); + lb.treesPerHectare(mockUtilVector(300)); + + lb.wholeStemVolume(mockUtilVector(7)); + lb.closeUtilizationVolumeByUtilization(mockUtilVector(6)); + lb.closeUtilizationVolumeNetOfDecayByUtilization(mockUtilVector(5)); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization(mockUtilVector(4)); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization(mockUtilVector(3)); + }); + }); + + var expectedState = new TestProcessingState(controlMap); + expectedState.setPolygon(expectedPolygon); + + jitterBank(expectedState.getPrimaryLayerProcessingState().getBank()); + jitterBank(expectedState.getVeteranLayerProcessingState().get().getBank()); + + var buf = new StringBuffer(); + TestUtils.writeModel(expectedState, buf, 3, "result"); + + System.out.print(buf.toString()); + + ProcessingState result = null; + + { + /* the following ProcessingState definition was generated */ + + /* the following Polygon definition was generated */ + + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("Test", 2024); + + pb.biogeoclimaticZone(Utils.getBec("CDF", controlMap)); + pb.forestInventoryZone("Z"); + + pb.inventoryTypeGroup(Optional.empty()); + pb.targetYear(Optional.empty()); + + pb.mode(Optional.empty()); + pb.percentAvailable(90.000000f); + + pb.addLayer(lb -> { + lb.layerType(ca.bc.gov.nrs.vdyp.model.LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(Optional.of(21)); + + lb.inventoryTypeGroup(Optional.of(34)); + + lb.loreyHeight(Utils.heightVector(3.637818f, 1.093304f)); + lb.treesPerHectare( + Utils.utilizationVector( + 110.634865f, 543.441101f, 114.492920f, 82.724403f, 207.127731f, 139.096069f + ) /* ALL does not match sum of bands */ + ); + lb.quadMeanDiameter( + Utils.utilizationVector( + 7.077106f, 21.115028f, 6.655489f, 0.913246f, 9.033722f, 4.512572f + ) /* ALL does not match sum of bands */ + ); + lb.baseArea( + Utils.utilizationVector( + 1.366447f, 3.151621f, 0.095879f, 0.617439f, 1.884147f, 0.554157f + ) /* ALL does not match sum of bands */ + ); + + lb.wholeStemVolume( + Utils.utilizationVector( + 5.334631f, 19.966562f, 5.480312f, 6.987250f, 6.435294f, 1.063708f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 2.618946f, 15.041424f, 2.638799f, 4.499437f, 5.583787f, 2.319401f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 3.991433f, 6.302918f, 0.886892f, 0.752737f, 2.971749f, 1.691540f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.839070f, 6.456025f, 1.002645f, 3.303863f, 1.460645f, 0.688872f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.475964f, 6.769507f, 1.762282f, 0.822518f, 2.253841f, 1.930866f + ) /* ALL does not match sum of bands */ + ); + + lb.addSpecies(sb -> { + sb.genus("MB"); + sb.genus(10); + + sb.breakageGroup(12); + sb.volumeGroup(14); + sb.decayGroup(13); + + sb.percentGenus(90.000000f); + + sb.addSp64Distribution("MB", 100.000000f); + + sb.loreyHeight(Utils.heightVector(2.855202f, 1.605718f)); + sb.treesPerHectare( + Utils.utilizationVector( + 125.306259f, 761.289429f, 230.860580f, 292.210693f, 24.196316f, 214.021866f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 3.165325f, 13.858646f, 3.579199f, 1.990815f, 8.177969f, 0.110663f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 1.160050f, 3.346419f, 0.181329f, 1.505020f, 1.597234f, 0.062836f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 3.731056f, 13.056453f, 3.364021f, 6.114161f, 2.041595f, 1.536676f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 5.699161f, 13.450598f, 3.575303f, 4.922951f, 1.132478f, 3.819867f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 1.892143f, 6.510414f, 1.845608f, 0.682118f, 1.801274f, 2.181414f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 1.738644f, 9.099073f, 2.595175f, 1.829268f, 2.783877f, 1.890754f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 2.852798f, 5.647935f, 1.407068f, 1.646782f, 2.481967f, 0.112118f + ) /* ALL does not match sum of bands */ + ); + + sb.addCompatibilityVariables(cvb -> { + + MatrixMap3 cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, + VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, + (uc, vv, lt) -> 0f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 1.510316f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 0.525790f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 8.338662f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 7.886145f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 4.603064f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 7.612512f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 2.805719f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 6.313797f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 1.959642f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 3.448284f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 1.792734f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 9.624868f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 8.656868f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 4.813280f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 4.865906f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 5.481640f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 4.209717f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 0.081496f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 6.328698f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 7.576355f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 6.998586f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 6.310371f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 3.153284f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 0.702512f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 5.716203f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 3.434496f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 3.710123f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 2.907161f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 8.718145f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 1.538193f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 8.057309f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 2.516958f + ); + MatrixMap2 cvBasalArea = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 6.162137f); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.VETERAN, 3.839646f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 3.743593f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.VETERAN, 1.149045f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 6.972487f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.VETERAN, 1.467351f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.PRIMARY, 9.086145f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.VETERAN, 2.915670f); + MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 1.961470f); + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.VETERAN, 1.709174f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 8.091248f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.VETERAN, 2.848068f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 6.279333f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.VETERAN, 7.807972f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.PRIMARY, 4.633992f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.VETERAN, 6.571403f); + + Map cvPrimaryLayerSmall = new HashMap<>(); + + cvPrimaryLayerSmall.put(UtilizationClassVariable.LOREY_HEIGHT, 3.055791f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.BASAL_AREA, 9.989216f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 5.399094f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.WHOLE_STEM_VOL, 7.960289f); + + cvb.cvVolume(cvVolume); + cvb.cvBasalArea(cvBasalArea); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); + }); + + sb.addSite(ib -> { + ib.ageTotal(Optional.of(40.000000f)); + ib.height(Optional.of(15.000000f)); + ib.siteCurveNumber(Optional.of(42)); + ib.siteIndex(Optional.of(4.000000f)); + ib.yearsToBreastHeight(Optional.of(5.000000f)); + }); + + }); + + lb.primaryGenus(Optional.of("MB")); + }); + pb.addLayer(lb -> { + lb.layerType(ca.bc.gov.nrs.vdyp.model.LayerType.VETERAN); + + lb.empiricalRelationshipParameterIndex(Optional.of(21)); + + lb.inventoryTypeGroup(Optional.of(34)); + + lb.loreyHeight(Utils.heightVector(3.175555f, 13.881115f)); + lb.treesPerHectare( + Utils.utilizationVector( + 196.516006f, 622.012695f, 79.691734f, 118.729874f, 141.539322f, 282.051727f + ) /* ALL does not match sum of bands */ + ); + lb.quadMeanDiameter( + Utils.utilizationVector( + 6.705582f, 14.483641f, 0.364845f, 4.686717f, 4.838438f, 4.593642f + ) /* ALL does not match sum of bands */ + ); + lb.baseArea( + Utils.utilizationVector( + 0.252516f, 2.816086f, 0.185386f, 0.994654f, 1.581418f, 0.054628f + ) /* ALL does not match sum of bands */ + ); + + lb.wholeStemVolume( + Utils.utilizationVector( + 0.953574f, 19.780567f, 2.692276f, 5.721595f, 4.523623f, 6.843073f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 4.622794f, 15.538091f, 4.212268f, 5.364257f, 2.368544f, 3.593022f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 1.419640f, 10.143337f, 4.880172f, 3.075758f, 0.385425f, 1.801982f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 3.100483f, 10.092585f, 1.466249f, 1.115289f, 3.914227f, 3.596821f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.542688f, 6.009441f, 1.121508f, 2.761546f, 1.293929f, 0.832458f + ) /* ALL does not match sum of bands */ + ); + + lb.addSpecies(sb -> { + sb.genus("S"); + sb.genus(15); + + sb.breakageGroup(12); + sb.volumeGroup(14); + sb.decayGroup(13); + + sb.percentGenus(90.000000f); + + sb.addSp64Distribution("S", 100.000000f); + + sb.loreyHeight(Utils.heightVector(1.661621f, 16.040403f)); + sb.treesPerHectare( + Utils.utilizationVector( + 241.617218f, 438.457031f, 87.019836f, 41.936874f, 281.616272f, 27.884029f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 3.965216f, 27.146305f, 7.451532f, 8.859586f, 1.708597f, 9.126590f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 0.630295f, 2.920992f, 1.591866f, 0.885472f, 0.250752f, 0.192902f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 2.081499f, 10.428626f, 0.189209f, 4.623718f, 3.838343f, 1.777356f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 1.693190f, 19.573658f, 2.429848f, 5.804972f, 5.911832f, 5.427006f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 4.492845f, 5.482807f, 1.719711f, 3.648051f, 0.070279f, 0.044766f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 3.238376f, 6.707272f, 0.068310f, 2.085977f, 1.212362f, 3.340622f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 1.679569f, 7.687805f, 1.750385f, 1.273551f, 2.064738f, 2.599131f + ) /* ALL does not match sum of bands */ + ); + + sb.addCompatibilityVariables(cvb -> { + + MatrixMap3 cvVolume = new MatrixMap3Impl<>( + UtilizationClass.UTIL_CLASSES, + VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, LayerType.ALL_USED, + (uc, vv, lt) -> 0f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 5.563822f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 4.803409f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 1.157501f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 3.106835f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 5.885086f + ); + cvVolume.put( + UtilizationClass.U75TO125, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 0.761642f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 8.951423f + ); + cvVolume.put( + UtilizationClass.U75TO125, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 8.886678f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 6.264751f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 6.610508f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 3.825809f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 7.061969f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 9.036251f + ); + cvVolume.put( + UtilizationClass.U125TO175, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 5.905811f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 8.336996f + ); + cvVolume.put( + UtilizationClass.U125TO175, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 1.468293f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 2.601936f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 0.075942f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 9.622362f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 6.406126f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 3.975361f + ); + cvVolume.put( + UtilizationClass.U175TO225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 8.291339f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 5.735347f + ); + cvVolume.put( + UtilizationClass.U175TO225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 4.349436f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.PRIMARY, 2.581649f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.WHOLE_STEM_VOL, + LayerType.VETERAN, 9.442972f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.PRIMARY, 8.217455f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL, + LayerType.VETERAN, 8.913176f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.PRIMARY, 6.591835f + ); + cvVolume.put( + UtilizationClass.OVER225, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, + LayerType.VETERAN, 1.291338f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.PRIMARY, 6.141067f + ); + cvVolume.put( + UtilizationClass.OVER225, + UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, + LayerType.VETERAN, 6.888435f + ); + MatrixMap2 cvBasalArea = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 4.881973f); + cvBasalArea.put(UtilizationClass.U75TO125, LayerType.VETERAN, 2.075868f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 3.614706f); + cvBasalArea.put(UtilizationClass.U125TO175, LayerType.VETERAN, 2.005780f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 7.379816f); + cvBasalArea.put(UtilizationClass.U175TO225, LayerType.VETERAN, 9.959743f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.PRIMARY, 8.553615f); + cvBasalArea.put(UtilizationClass.OVER225, LayerType.VETERAN, 9.691236f); + MatrixMap2 cvQuadraticMeanDiameter = new MatrixMap2Impl<>( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (uc, lt) -> 0f + ); + + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.PRIMARY, 3.397471f); + cvQuadraticMeanDiameter.put(UtilizationClass.U75TO125, LayerType.VETERAN, 5.050452f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.PRIMARY, 7.580298f); + cvQuadraticMeanDiameter.put(UtilizationClass.U125TO175, LayerType.VETERAN, 0.937272f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.PRIMARY, 7.260781f); + cvQuadraticMeanDiameter.put(UtilizationClass.U175TO225, LayerType.VETERAN, 5.087435f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.PRIMARY, 5.085622f); + cvQuadraticMeanDiameter.put(UtilizationClass.OVER225, LayerType.VETERAN, 8.556303f); + + Map cvPrimaryLayerSmall = new HashMap<>(); + + cvPrimaryLayerSmall.put(UtilizationClassVariable.LOREY_HEIGHT, 7.754099f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.BASAL_AREA, 7.710728f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.QUAD_MEAN_DIAMETER, 3.760872f); + cvPrimaryLayerSmall.put(UtilizationClassVariable.WHOLE_STEM_VOL, 6.581034f); + + cvb.cvVolume(cvVolume); + cvb.cvBasalArea(cvBasalArea); + cvb.cvQuadraticMeanDiameter(cvQuadraticMeanDiameter); + cvb.cvPrimaryLayerSmall(cvPrimaryLayerSmall); + }); + + }); + + lb.primaryGenus(Optional.of("S")); + }); + }); + + /* End of generated Polygon definition */ + var state = new TestProcessingState(controlMap); + state.setPolygon(polygon); + var primaryBank = state.getPrimaryLayerProcessingState().getBank(); + System.arraycopy(new float[] { 0.000000f, 37.029385f }, 0, primaryBank.ageTotals, 0, 2); + System.arraycopy(new float[] { 0.000000f, 0.000000f }, 0, primaryBank.percentagesOfForestedLand, 0, 2); + System.arraycopy( + new float[][] { { 1.205777f, 3.280845f, 0.169003f, 1.358052f, 1.693087f, 0.063084f }, + { 1.212356f, 3.301440f, 0.192023f, 1.584733f, 1.700492f, 0.064628f } }, + 0, primaryBank.basalAreas, 0, 2 + ); + System.arraycopy(new String[] { null, "MB" }, 0, primaryBank.speciesNames, 0, 2); + System.arraycopy(new float[] { 0.000000f, 3.794725f }, 0, primaryBank.siteIndices, 0, 2); + System.arraycopy(new float[] { 0.000000f, 14.770509f }, 0, primaryBank.dominantHeights, 0, 2); + System.arraycopy(new float[] { 0.000000f, 31.781885f }, 0, primaryBank.yearsAtBreastHeight, 0, 2); + System.arraycopy(new float[] { 0.000000f, 4.648443f }, 0, primaryBank.yearsToBreastHeight, 0, 2); + System.arraycopy(new int[] { -1, 41 }, 0, primaryBank.siteCurveNumbers, 0, 2); + var veteranBank = state.getVeteranLayerProcessingState().get().getBank(); + System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.ageTotals, 0, 2); + System.arraycopy(new float[] { 0.000000f, 0.000000f }, 0, veteranBank.percentagesOfForestedLand, 0, 2); + System.arraycopy( + new float[][] { { 0.634654f, 3.064580f, 1.607465f, 0.930330f, 0.242154f, 0.211027f }, + { 0.658044f, 3.157326f, 1.556144f, 0.908407f, 0.226441f, 0.190807f } }, + 0, veteranBank.basalAreas, 0, 2 + ); + System.arraycopy(new String[] { null, "S" }, 0, veteranBank.speciesNames, 0, 2); + System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.siteIndices, 0, 2); + System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.dominantHeights, 0, 2); + System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.yearsAtBreastHeight, 0, 2); + System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.yearsToBreastHeight, 0, 2); + System.arraycopy(new int[] { -1, -10 }, 0, veteranBank.siteCurveNumbers, 0, 2); + + /* End of generated ProcessingState definition */ + } + + assertThat(result, deepEquals((ProcessingState) expectedState)); + + } + } @Nested diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index a7c9a8dc3..b46bcb338 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -43,6 +43,7 @@ import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.common.ValueOrMarker; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.value.ValueParseException; @@ -65,6 +66,7 @@ import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; /** @@ -1402,7 +1404,8 @@ public static Matcher deepEquals(Bank expected) { return allOf(matchers); } - public static Matcher deepEquals(TestProcessingState expectedState) { + public static > + Matcher> deepEquals(ProcessingState expectedState) { return allOf( hasProperty( "currentPolygon", @@ -1410,6 +1413,7 @@ public static Matcher deepEquals(TestProcessingStat "polygonIdentifier", isPolyId(expectedState.getCurrentPolygon().getPolygonIdentifier()) ) ), + hasProperty("primaryLayerProcessingState", deepEquals(expectedState.getPrimaryLayerProcessingState())), hasProperty( "veteranLayerProcessingState", optional(expectedState.getVeteranLayerProcessingState(), v -> deepEquals(v)) From d78f01e6ac48a599b02af5964fca946783d65d9b Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Wed, 4 Dec 2024 17:27:14 -0800 Subject: [PATCH 44/45] Dump state for processing state tests --- .../java/ca/bc/gov/nrs/vdyp/test/TestUtils.java | 8 ++++---- .../ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java | 14 +++++++------- .../java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java | 10 +++++++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index 4eaddab36..50bba9ad3 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -1037,14 +1037,14 @@ public static void writeModel(ProcessingState expected, Appendable out, in writeModel(poly, out, indent, "var polygon"); - line(out, indent, "var state = new TestProcessingState(controlMap);"); - line(out, indent, "state.setPolygon(polygon);"); - line(out, indent, "var primaryBank = state.getPrimaryLayerProcessingState().getBank();"); + line(out, indent, "%s = new TestProcessingState(controlMap);", assignTo); + line(out, indent, "%s.setPolygon(polygon);", assignTo); + line(out, indent, "var primaryBank = %s.getPrimaryLayerProcessingState().getBank();", assignTo); writeBankConfig(expected.getPrimaryLayerProcessingState().getBank(), out, indent, "primaryBank"); expected.getVeteranLayerProcessingState().ifPresent(vetState -> { - line(out, indent, "var veteranBank = state.getVeteranLayerProcessingState().get().getBank();"); + line(out, indent, "var veteranBank = %s.getVeteranLayerProcessingState().get().getBank();", assignTo); writeBankConfig(vetState.getBank(), out, indent, "veteranBank"); }); diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java index 45f237c26..d2e5b3902 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtilsTest.java @@ -158,7 +158,7 @@ void testBank() throws IOException { assertThat(result, deepEquals(expected)); } - public void jitterBank(Bank expected) { + void jitterBank(Bank expected) { TestUtils.jitterArray(expected.ageTotals, rand); TestUtils.jitterArray(expected.basalAreas, rand); TestUtils.jitterArray(expected.dominantHeights, rand); @@ -304,7 +304,7 @@ void testProcessingState() throws IOException, ProcessingException { System.out.print(buf.toString()); - ProcessingState result = null; + ProcessingState result = null; { /* the following ProcessingState definition was generated */ @@ -912,9 +912,9 @@ void testProcessingState() throws IOException, ProcessingException { }); /* End of generated Polygon definition */ - var state = new TestProcessingState(controlMap); - state.setPolygon(polygon); - var primaryBank = state.getPrimaryLayerProcessingState().getBank(); + result = new TestProcessingState(controlMap); + result.setPolygon(polygon); + var primaryBank = result.getPrimaryLayerProcessingState().getBank(); System.arraycopy(new float[] { 0.000000f, 37.029385f }, 0, primaryBank.ageTotals, 0, 2); System.arraycopy(new float[] { 0.000000f, 0.000000f }, 0, primaryBank.percentagesOfForestedLand, 0, 2); System.arraycopy( @@ -928,7 +928,7 @@ void testProcessingState() throws IOException, ProcessingException { System.arraycopy(new float[] { 0.000000f, 31.781885f }, 0, primaryBank.yearsAtBreastHeight, 0, 2); System.arraycopy(new float[] { 0.000000f, 4.648443f }, 0, primaryBank.yearsToBreastHeight, 0, 2); System.arraycopy(new int[] { -1, 41 }, 0, primaryBank.siteCurveNumbers, 0, 2); - var veteranBank = state.getVeteranLayerProcessingState().get().getBank(); + var veteranBank = result.getVeteranLayerProcessingState().get().getBank(); System.arraycopy(new float[] { 0.000000f, Float.NaN }, 0, veteranBank.ageTotals, 0, 2); System.arraycopy(new float[] { 0.000000f, 0.000000f }, 0, veteranBank.percentagesOfForestedLand, 0, 2); System.arraycopy( @@ -946,7 +946,7 @@ void testProcessingState() throws IOException, ProcessingException { /* End of generated ProcessingState definition */ } - assertThat(result, deepEquals((ProcessingState) expectedState)); + assertThat(result, deepEquals(expectedState)); } diff --git a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index b46bcb338..050f0feb1 100644 --- a/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/lib/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -195,7 +195,9 @@ public void describeMismatch(Object item, Description description) { description.appendText("Not present"); return; } - delegate.describeMismatch(item, description); + + description.appendText("was present but "); + delegate.describeMismatch( ((Optional) item).get(), description); } }; @@ -576,7 +578,10 @@ protected boolean matchesSafely(Float item, Description mismatchDescription) { } public static Matcher closeTo(float expected) { - return closeTo(expected, currentEpsilon); + if (Float.isFinite(expected)) { + return closeTo(expected, currentEpsilon); + } + return equalTo(expected); } public static Matcher closeTo(float expected, float threshold) { @@ -1370,7 +1375,6 @@ public static Matcher arrayCloseTo3D(float[][][] expected) { } public static > Matcher deepEquals(T expected) { - expected.getBank(); return allOf( hasProperty( "polygon", From 29b662a81690a91f09914abe03c500a63a60faee Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Mon, 9 Dec 2024 13:20:35 -0800 Subject: [PATCH 45/45] Working on refactoring CVSET to move to common package --- .../vdyp/application/ProcessingEngine.java | 268 +++++++++++- .../LayerProcessingState.java | 4 + .../forward/ForwardLayerProcessingState.java | 6 +- .../vdyp/forward/ForwardProcessingEngine.java | 291 ++----------- .../vdyp/forward/ForwardProcessingState.java | 3 +- .../forward/Grow10StoreSpeciesDetails.java | 4 +- .../Grow11UpdateCompatibilityVariables.java | 4 +- ...Grow1CalculateDominantHeightDeltaTest.java | 4 +- .../Grow2CalculateBasalAreaDeltaTest.java | 8 +- ...ow3CalculateQuadMeanDiameterDeltaTest.java | 14 +- ...row4CalculateLoreyHeightEstimatesTest.java | 6 +- .../vdyp/forward/Grow5SpeciesBaDqTphTest.java | 8 +- .../forward/Grow6TreesPerHectareTest.java | 4 +- .../forward/Grow7QuadMeanDiameterTest.java | 4 +- .../Grow8PerSpeciesLoreyHeightTest.java | 6 +- .../Grow9PercentagesOfForestedLand.java | 4 +- ...inaryForwardProcessingEngineStepsTest.java | 20 +- ...liminarySetCompatibilityVariablesTest.java | 406 +++++++++++++++++- .../vdyp/forward/ProcessPolygonBasicTest.java | 4 +- 19 files changed, 766 insertions(+), 302 deletions(-) diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java index 8a4e5184e..7d9521f45 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/application/ProcessingEngine.java @@ -1,9 +1,68 @@ package ca.bc.gov.nrs.vdyp.application; +import static ca.bc.gov.nrs.vdyp.math.FloatMath.clamp; +import static ca.bc.gov.nrs.vdyp.math.FloatMath.log; + +import java.util.HashMap; +import java.util.Map; + +import ca.bc.gov.nrs.vdyp.common.ReconcilationMethods; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; +import ca.bc.gov.nrs.vdyp.controlmap.ResolvedControlMap; +import ca.bc.gov.nrs.vdyp.model.Coefficients; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2; +import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3; +import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.UtilizationVector; +import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; +import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; -public abstract class ProcessingEngine> { +public abstract class ProcessingEngine, L extends LayerProcessingState, E extends ProcessingEngine.ExecutionStep> { + + public static final float[] DEFAULT_QUAD_MEAN_DIAMETERS = new float[] { Float.NaN, 10.0f, 15.0f, 20.0f, 25.0f }; + public static final float V_BASE_MIN = 0.1f; + public static final float B_BASE_MIN = 0.01f; + public static float calculateCompatibilityVariable(float actualVolume, float baseVolume, float staticVolume) { + + float staticRatio = staticVolume / baseVolume; + float staticLogit; + if (staticRatio <= 0.0f) { + staticLogit = -7.0f; + } else if (staticRatio >= 1.0f) { + staticLogit = 7.0f; + } else { + staticLogit = clamp(log(staticRatio / (1.0f - staticRatio)), -7.0f, 7.0f); + } + + float actualRatio = actualVolume / baseVolume; + float actualLogit; + if (actualRatio <= 0.0f) { + actualLogit = -7.0f; + } else if (actualRatio >= 1.0f) { + actualLogit = 7.0f; + } else { + actualLogit = clamp(log(actualRatio / (1.0f - actualRatio)), -7.0f, 7.0f); + } + + return actualLogit - staticLogit; + } + + public S fps; + + protected ProcessingEngine(Map controlMap) throws ProcessingException { + + this.fps = getState(controlMap); + } + + protected abstract S getState(Map controlMap) throws ProcessingException; /** * Run all steps of the engine on the given polygon up to and including the given lastStep. @@ -67,4 +126,211 @@ public void processPolygon(VdypPolygon polygon) throws ProcessingException { processPolygon(polygon, getLastStep()); } + /** + * CVSET1 - computes cvVolume, cvBasalArea, cvQuadraticMeanDiameter and cvSmall and assigns them to the current + * LayerProcessingState. + * + * @throws ProcessingException + */ + @SuppressWarnings("unchecked") + protected void setCompatibilityVariables() throws ProcessingException { + + Coefficients aAdjust = new Coefficients(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, 1); + + var growthDetails = fps.getControlMap().getForwardControlVariables(); + var lps = fps.getPrimaryLayerProcessingState(); + Bank bank = lps.getBank(); + + // Note: L1COM2 (INL1VGRP, INL1DGRP, INL1BGRP) is initialized when + // PolygonProcessingState (volumeEquationGroups, decayEquationGroups + // breakageEquationGroups, respectively) is constructed. Copying + // the values into LCOM1 is not necessary. Note, however, that + // VolumeEquationGroup 10 is mapped to 11 (VGRPFIND) - this is done + // when volumeEquationGroups is built (i.e., when the equivalent to + // INL1VGRP is built, rather than when LCOM1 VGRPL is built in the + // original code.) + + var cvVolume = new MatrixMap3[lps.getNSpecies() + 1]; + var cvBasalArea = new MatrixMap2[lps.getNSpecies() + 1]; + var cvQuadraticMeanDiameter = new MatrixMap2[lps.getNSpecies() + 1]; + var cvSmall = new HashMap[lps.getNSpecies() + 1]; + + for (int s : lps.getIndices()) { + + String genusName = bank.speciesNames[s]; + + float spLoreyHeight_All = bank.loreyHeights[s][UtilizationClass.ALL.ordinal()]; + + UtilizationVector basalAreas = Utils.utilizationVector(); + UtilizationVector wholeStemVolumes = Utils.utilizationVector(); + UtilizationVector closeUtilizationVolumes = Utils.utilizationVector(); + UtilizationVector closeUtilizationVolumesNetOfDecay = Utils.utilizationVector(); + UtilizationVector closeUtilizationVolumesNetOfDecayAndWaste = Utils.utilizationVector(); + UtilizationVector quadMeanDiameters = Utils.utilizationVector(); + UtilizationVector treesPerHectare = Utils.utilizationVector(); + + cvVolume[s] = new MatrixMap3Impl( + UtilizationClass.UTIL_CLASSES, VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, + LayerType.ALL_USED, (k1, k2, k3) -> 0f + ); + cvBasalArea[s] = new MatrixMap2Impl( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (k1, k2) -> 0f + ); + cvQuadraticMeanDiameter[s] = new MatrixMap2Impl( + UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (k1, k2) -> 0f + ); + + for (UtilizationClass uc : UtilizationClass.ALL_BUT_SMALL) { + + basalAreas.setCoe(uc.index, bank.basalAreas[s][uc.ordinal()]); + wholeStemVolumes.setCoe(uc.index, bank.wholeStemVolumes[s][uc.ordinal()]); + closeUtilizationVolumes.setCoe(uc.index, bank.closeUtilizationVolumes[s][uc.ordinal()]); + closeUtilizationVolumesNetOfDecay.setCoe(uc.index, bank.cuVolumesMinusDecay[s][uc.ordinal()]); + closeUtilizationVolumesNetOfDecayAndWaste + .setCoe(uc.index, bank.cuVolumesMinusDecayAndWastage[s][uc.ordinal()]); + + quadMeanDiameters.setCoe(uc.index, bank.quadMeanDiameters[s][uc.ordinal()]); + if (uc != UtilizationClass.ALL && quadMeanDiameters.getCoe(uc.index) <= 0.0f) { + quadMeanDiameters.setCoe(uc.index, DEFAULT_QUAD_MEAN_DIAMETERS[uc.ordinal()]); + } + } + + for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { + + float adjustment; + float baseVolume; + + // Volume less decay and waste + adjustment = 0.0f; + baseVolume = bank.cuVolumesMinusDecay[s][uc.ordinal()]; + + if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { + + // EMP094 + fps.getEstimators().estimateNetDecayAndWasteVolume( + lps.getBecZone().getRegion(), uc, aAdjust, bank.speciesNames[s], spLoreyHeight_All, + quadMeanDiameters, closeUtilizationVolumes, closeUtilizationVolumesNetOfDecay, + closeUtilizationVolumesNetOfDecayAndWaste + ); + + float actualVolume = bank.cuVolumesMinusDecayAndWastage[s][uc.ordinal()]; + float staticVolume = closeUtilizationVolumesNetOfDecayAndWaste.getCoe(uc.index); + adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); + } + + cvVolume[s].put( + uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, + adjustment + ); + + // Volume less decay + adjustment = 0.0f; + baseVolume = bank.closeUtilizationVolumes[s][uc.ordinal()]; + + if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { + + // EMP093 + int decayGroup = lps.getDecayEquationGroups()[s]; + fps.getEstimators().estimateNetDecayVolume( + bank.speciesNames[s], lps.getBecZone().getRegion(), uc, aAdjust, decayGroup, + lps.getPrimarySpeciesAgeAtBreastHeight(), quadMeanDiameters, closeUtilizationVolumes, + closeUtilizationVolumesNetOfDecay + ); + + float actualVolume = bank.cuVolumesMinusDecay[s][uc.ordinal()]; + float staticVolume = closeUtilizationVolumesNetOfDecay.getCoe(uc.index); + adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); + } + + cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, LayerType.PRIMARY, adjustment); + + // Volume + adjustment = 0.0f; + baseVolume = bank.wholeStemVolumes[s][uc.ordinal()]; + + if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { + + // EMP092 + int volumeGroup = lps.getVolumeEquationGroups()[s]; + fps.getEstimators().estimateCloseUtilizationVolume( + uc, aAdjust, volumeGroup, spLoreyHeight_All, quadMeanDiameters, wholeStemVolumes, + closeUtilizationVolumes + ); + + float actualVolume = bank.closeUtilizationVolumes[s][uc.ordinal()]; + float staticVolume = closeUtilizationVolumes.getCoe(uc.index); + adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); + } + + cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, adjustment); + } + + int primarySpeciesVolumeGroup = lps.getVolumeEquationGroups()[s]; + float primarySpeciesQMDAll = bank.quadMeanDiameters[s][UC_ALL_INDEX]; + var wholeStemVolume = bank.treesPerHectare[s][UC_ALL_INDEX] * fps.getEstimators() + .estimateWholeStemVolumePerTree(primarySpeciesVolumeGroup, spLoreyHeight_All, primarySpeciesQMDAll); + + wholeStemVolumes.setCoe(UC_ALL_INDEX, wholeStemVolume); + + fps.getEstimators().estimateWholeStemVolume( + UtilizationClass.ALL, 0.0f, primarySpeciesVolumeGroup, spLoreyHeight_All, quadMeanDiameters, + basalAreas, wholeStemVolumes + ); + + for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { + float adjustment = 0.0f; + float basalArea = basalAreas.getCoe(uc.index); + if (growthDetails.allowCalculation(basalArea, B_BASE_MIN, (l, r) -> l > r)) { + adjustment = calculateWholeStemVolume( + bank.wholeStemVolumes[s][uc.ordinal()], basalArea, wholeStemVolumes.getCoe(uc.index) + ); + } + + cvVolume[s].put(uc, UtilizationClassVariable.WHOLE_STEM_VOL, LayerType.PRIMARY, adjustment); + } + + fps.getEstimators().estimateQuadMeanDiameterByUtilization(lps.getBecZone(), quadMeanDiameters, genusName); + + fps.getEstimators() + .estimateBaseAreaByUtilization(lps.getBecZone(), quadMeanDiameters, basalAreas, genusName); + + // Calculate trees-per-hectare per utilization + treesPerHectare.setCoe(UtilizationClass.ALL.index, bank.treesPerHectare[s][UC_ALL_INDEX]); + for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { + treesPerHectare.setCoe( + uc.index, + BaseAreaTreeDensityDiameter + .treesPerHectare(basalAreas.getCoe(uc.index), quadMeanDiameters.getCoe(uc.index)) + ); + } + + ReconcilationMethods.reconcileComponents(basalAreas, treesPerHectare, quadMeanDiameters); + + for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { + float baCvValue = bank.basalAreas[s][uc.ordinal()] - basalAreas.getCoe(uc.index); + cvBasalArea[s].put(uc, LayerType.PRIMARY, baCvValue); + + float originalQmd = bank.quadMeanDiameters[s][uc.ordinal()]; + float adjustedQmd = quadMeanDiameters.getCoe(uc.index); + + float qmdCvValue; + if (growthDetails.allowCalculation(() -> originalQmd < B_BASE_MIN)) { + qmdCvValue = 0.0f; + } else if (originalQmd > 0 && adjustedQmd > 0) { + qmdCvValue = originalQmd - adjustedQmd; + } else { + qmdCvValue = 0.0f; + } + + cvQuadraticMeanDiameter[s].put(uc, LayerType.PRIMARY, qmdCvValue); + } + + // Small components + + cvSmall[s] = calculateSmallCompatibilityVariables(s, growthDetails); + } + + lps.setCompatibilityVariableDetails(cvVolume, cvBasalArea, cvQuadraticMeanDiameter, cvSmall); + } + } diff --git a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java index 4e9cda43b..d793bae49 100644 --- a/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java +++ b/lib/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/processing_state/LayerProcessingState.java @@ -162,4 +162,8 @@ public Map getCvPrimaryLayerSmall(int speciesIn } + public int[] getIndices() { + return getBank().getIndices(); + } + } \ No newline at end of file diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java index 238a4e4f4..a14c472eb 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardLayerProcessingState.java @@ -19,7 +19,7 @@ import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; -class ForwardLayerProcessingState extends LayerProcessingState { +public class ForwardLayerProcessingState extends LayerProcessingState { private static final String PRIMARY_SPECIES_DETAILS_CAN_BE_SET_ONCE_ONLY = "PrimarySpeciesDetails can be set once only"; private static final String SITE_CURVE_NUMBERS_CAN_BE_SET_ONCE_ONLY = "SiteCurveNumbers can be set once only"; @@ -119,10 +119,6 @@ protected Predicate getBankFilter() { return s -> s.getBaseAreaByUtilization().get(UtilizationClass.ALL) >= ForwardProcessingEngine.MIN_BASAL_AREA; } - public int[] getIndices() { - return getBank().getIndices(); - } - public int getPrimarySpeciesIndex() { if (!areRankingDetailsSet) { throw new IllegalStateException("unset primarySpeciesIndex"); diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java index 33432f39f..ea3dafdab 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingEngine.java @@ -1,6 +1,5 @@ package ca.bc.gov.nrs.vdyp.forward; -import static ca.bc.gov.nrs.vdyp.math.FloatMath.clamp; import static ca.bc.gov.nrs.vdyp.math.FloatMath.exp; import static ca.bc.gov.nrs.vdyp.math.FloatMath.log; import static ca.bc.gov.nrs.vdyp.math.FloatMath.pow; @@ -27,7 +26,6 @@ import ca.bc.gov.nrs.vdyp.application.StandProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.EstimationMethods; -import ca.bc.gov.nrs.vdyp.common.ReconcilationMethods; import ca.bc.gov.nrs.vdyp.common.Reference; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; @@ -37,6 +35,7 @@ import ca.bc.gov.nrs.vdyp.common_calculators.custom_exceptions.SpeciesErrorException; import ca.bc.gov.nrs.vdyp.common_calculators.enumerations.SiteIndexAgeType; import ca.bc.gov.nrs.vdyp.common_calculators.enumerations.SiteIndexEquation; +import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMap; import ca.bc.gov.nrs.vdyp.forward.model.ControlVariable; import ca.bc.gov.nrs.vdyp.forward.model.ForwardControlVariables; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; @@ -51,23 +50,19 @@ import ca.bc.gov.nrs.vdyp.model.ComponentSizeLimits; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; -import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; -import ca.bc.gov.nrs.vdyp.model.MatrixMap3; -import ca.bc.gov.nrs.vdyp.model.MatrixMap3Impl; import ca.bc.gov.nrs.vdyp.model.ModelCoefficients; import ca.bc.gov.nrs.vdyp.model.NonprimaryHLCoefficients; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.SiteCurveAgeMaximum; import ca.bc.gov.nrs.vdyp.model.Sp64Distribution; import ca.bc.gov.nrs.vdyp.model.UtilizationClass; -import ca.bc.gov.nrs.vdyp.model.UtilizationVector; -import ca.bc.gov.nrs.vdyp.model.VdypCompatibilityVariables; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VolumeComputeMode; import ca.bc.gov.nrs.vdyp.model.variables.UtilizationClassVariable; import ca.bc.gov.nrs.vdyp.processing_state.Bank; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; import ca.bc.gov.nrs.vdyp.si32.site.SiteTool; /** @@ -75,11 +70,12 @@ * processPolygon for each polygon to be processed. All calls to processPolygon are entirely * independent of one another, allowing (different) polygons to the processed in parallel. */ -public class ForwardProcessingEngine extends ProcessingEngine { +public class ForwardProcessingEngine extends + ProcessingEngine { private static final Logger logger = LoggerFactory.getLogger(ForwardProcessingEngine.class); - private static final int UC_ALL_INDEX = UtilizationClass.ALL.ordinal(); + public static final int UC_ALL_INDEX = UtilizationClass.ALL.ordinal(); private static final int UC_SMALL_INDEX = UtilizationClass.SMALL.ordinal(); public static final float MIN_BASAL_AREA = 0.001f; @@ -130,8 +126,6 @@ public ForwardExecutionStep successor() { } - /* pp */ final ForwardProcessingState fps; - /** The entity to which result information is written */ private Optional outputWriter = Optional.empty(); @@ -139,7 +133,7 @@ public ForwardExecutionStep successor() { public ForwardProcessingEngine(Map controlMap, Optional outputWriter) throws ProcessingException { - this.fps = new ForwardProcessingState(controlMap); + super(controlMap); this.outputWriter = outputWriter; int cv7Value = this.fps.getControlMap().getForwardControlVariables() @@ -617,7 +611,7 @@ private void grow( return; } - private void updateVeteranSpeciesAges(ForwardLayerProcessingState vlps) { + private void updateVeteranSpeciesAges(LayerProcessingState vlps) { for (int i : vlps.getIndices()) { vlps.getBank().ageTotals[i] += 1; @@ -650,7 +644,7 @@ private void writeCheckpoint(int year) { */ void growUsingNoSpeciesDynamics(float baChangeRate, float tphChangeRate) { - ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); for (int i : lps.getIndices()) { @@ -720,7 +714,7 @@ boolean growUsingPartialSpeciesDynamics( float baStart, float baDelta, float dqStart, float dqDelta, float tphStart, float[] hlStart ) throws ProcessingException { - ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); Region polygonRegion = fps.getCurrentBecZone().getRegion(); @@ -1491,7 +1485,7 @@ private float growBasalAreaForPrimarySpecies( * * @throws ProcessingException */ - private void calculateSmallComponentYields(ForwardLayerProcessingState lps) throws ProcessingException { + private void calculateSmallComponentYields(LayerProcessingState lps) throws ProcessingException { Bank bank = lps.getBank(); @@ -1603,7 +1597,7 @@ private void calculateSmallComponentYields(ForwardLayerProcessingState lps) thro * @param pspLhStart primary species Lorey height at end */ void growLoreyHeights( - ForwardLayerProcessingState lps, float dhStart, float dhEnd, float pspTphStart, float pspTphEnd, + LayerProcessingState lps, float dhStart, float dhEnd, float pspTphStart, float pspTphEnd, float pspLhStart ) { Bank bank = lps.getBank(); @@ -1729,7 +1723,7 @@ float calculateQuadMeanDiameterDelta( Optional veteranBaEnd, float dhDelta, Reference dqGrowthLimitApplied ) throws StandProcessingException { - var lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); var becZone = lps.getBecZone(); float[] speciesProportionsByBasalArea = getSpeciesProportionsByBasalAreaAtStartOfYear(); @@ -1903,7 +1897,7 @@ private float calculateQuadMeanDiameterGrowthEmpirical( */ private float[] getSpeciesProportionsByBasalAreaAtStartOfYear() { - ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float[] speciesProportionsByBasalArea = new float[lps.getNSpecies() + 1]; @@ -1932,7 +1926,7 @@ float calculateBasalAreaDelta( ) throws StandProcessingException { ForwardDebugSettings debugSettings = fps.getControlMap().getDebugSettings(); - ForwardLayerProcessingState lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); float[] speciesProportionsByBasalArea = getSpeciesProportionsByBasalAreaAtStartOfYear(); @@ -2407,217 +2401,6 @@ private void writeCurrentPolygon(int startYear, int currentYear, int endYear) th } } - private static final float[] DEFAULT_QUAD_MEAN_DIAMETERS = new float[] { Float.NaN, 10.0f, 15.0f, 20.0f, 25.0f }; - private static final float V_BASE_MIN = 0.1f; - private static final float B_BASE_MIN = 0.01f; - - /** - * CVSET1 - computes cvVolume, cvBasalArea, cvQuadraticMeanDiameter and cvSmall and assigns them to the current - * LayerProcessingState. - * - * @throws ProcessingException - */ - @SuppressWarnings("unchecked") - void setCompatibilityVariables() throws ProcessingException { - - Coefficients aAdjust = new Coefficients(new float[] { 0.0f, 0.0f, 0.0f, 0.0f }, 1); - - var growthDetails = fps.getControlMap().getForwardControlVariables(); - var lps = fps.getPrimaryLayerProcessingState(); - Bank bank = lps.getBank(); - - // Note: L1COM2 (INL1VGRP, INL1DGRP, INL1BGRP) is initialized when - // PolygonProcessingState (volumeEquationGroups, decayEquationGroups - // breakageEquationGroups, respectively) is constructed. Copying - // the values into LCOM1 is not necessary. Note, however, that - // VolumeEquationGroup 10 is mapped to 11 (VGRPFIND) - this is done - // when volumeEquationGroups is built (i.e., when the equivalent to - // INL1VGRP is built, rather than when LCOM1 VGRPL is built in the - // original code.) - - var cvVolume = new MatrixMap3[lps.getNSpecies() + 1]; - var cvBasalArea = new MatrixMap2[lps.getNSpecies() + 1]; - var cvQuadraticMeanDiameter = new MatrixMap2[lps.getNSpecies() + 1]; - var cvSmall = new HashMap[lps.getNSpecies() + 1]; - - for (int s : lps.getIndices()) { - - String genusName = bank.speciesNames[s]; - - float spLoreyHeight_All = bank.loreyHeights[s][UtilizationClass.ALL.ordinal()]; - - UtilizationVector basalAreas = Utils.utilizationVector(); - UtilizationVector wholeStemVolumes = Utils.utilizationVector(); - UtilizationVector closeUtilizationVolumes = Utils.utilizationVector(); - UtilizationVector closeUtilizationVolumesNetOfDecay = Utils.utilizationVector(); - UtilizationVector closeUtilizationVolumesNetOfDecayAndWaste = Utils.utilizationVector(); - UtilizationVector quadMeanDiameters = Utils.utilizationVector(); - UtilizationVector treesPerHectare = Utils.utilizationVector(); - - cvVolume[s] = new MatrixMap3Impl( - UtilizationClass.UTIL_CLASSES, VdypCompatibilityVariables.VOLUME_UTILIZATION_VARIABLES, - LayerType.ALL_USED, (k1, k2, k3) -> 0f - ); - cvBasalArea[s] = new MatrixMap2Impl( - UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (k1, k2) -> 0f - ); - cvQuadraticMeanDiameter[s] = new MatrixMap2Impl( - UtilizationClass.UTIL_CLASSES, LayerType.ALL_USED, (k1, k2) -> 0f - ); - - for (UtilizationClass uc : UtilizationClass.ALL_BUT_SMALL) { - - basalAreas.setCoe(uc.index, bank.basalAreas[s][uc.ordinal()]); - wholeStemVolumes.setCoe(uc.index, bank.wholeStemVolumes[s][uc.ordinal()]); - closeUtilizationVolumes.setCoe(uc.index, bank.closeUtilizationVolumes[s][uc.ordinal()]); - closeUtilizationVolumesNetOfDecay.setCoe(uc.index, bank.cuVolumesMinusDecay[s][uc.ordinal()]); - closeUtilizationVolumesNetOfDecayAndWaste - .setCoe(uc.index, bank.cuVolumesMinusDecayAndWastage[s][uc.ordinal()]); - - quadMeanDiameters.setCoe(uc.index, bank.quadMeanDiameters[s][uc.ordinal()]); - if (uc != UtilizationClass.ALL && quadMeanDiameters.getCoe(uc.index) <= 0.0f) { - quadMeanDiameters.setCoe(uc.index, DEFAULT_QUAD_MEAN_DIAMETERS[uc.ordinal()]); - } - } - - for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - - float adjustment; - float baseVolume; - - // Volume less decay and waste - adjustment = 0.0f; - baseVolume = bank.cuVolumesMinusDecay[s][uc.ordinal()]; - - if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { - - // EMP094 - fps.getEstimators().estimateNetDecayAndWasteVolume( - lps.getBecZone().getRegion(), uc, aAdjust, bank.speciesNames[s], spLoreyHeight_All, - quadMeanDiameters, closeUtilizationVolumes, closeUtilizationVolumesNetOfDecay, - closeUtilizationVolumesNetOfDecayAndWaste - ); - - float actualVolume = bank.cuVolumesMinusDecayAndWastage[s][uc.ordinal()]; - float staticVolume = closeUtilizationVolumesNetOfDecayAndWaste.getCoe(uc.index); - adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); - } - - cvVolume[s].put( - uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY_LESS_WASTAGE, LayerType.PRIMARY, - adjustment - ); - - // Volume less decay - adjustment = 0.0f; - baseVolume = bank.closeUtilizationVolumes[s][uc.ordinal()]; - - if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { - - // EMP093 - int decayGroup = lps.getDecayEquationGroups()[s]; - fps.getEstimators().estimateNetDecayVolume( - bank.speciesNames[s], lps.getBecZone().getRegion(), uc, aAdjust, decayGroup, - lps.getPrimarySpeciesAgeAtBreastHeight(), quadMeanDiameters, closeUtilizationVolumes, - closeUtilizationVolumesNetOfDecay - ); - - float actualVolume = bank.cuVolumesMinusDecay[s][uc.ordinal()]; - float staticVolume = closeUtilizationVolumesNetOfDecay.getCoe(uc.index); - adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); - } - - cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL_LESS_DECAY, LayerType.PRIMARY, adjustment); - - // Volume - adjustment = 0.0f; - baseVolume = bank.wholeStemVolumes[s][uc.ordinal()]; - - if (growthDetails.allowCalculation(baseVolume, V_BASE_MIN, (l, r) -> l > r)) { - - // EMP092 - int volumeGroup = lps.getVolumeEquationGroups()[s]; - fps.getEstimators().estimateCloseUtilizationVolume( - uc, aAdjust, volumeGroup, spLoreyHeight_All, quadMeanDiameters, wholeStemVolumes, - closeUtilizationVolumes - ); - - float actualVolume = bank.closeUtilizationVolumes[s][uc.ordinal()]; - float staticVolume = closeUtilizationVolumes.getCoe(uc.index); - adjustment = calculateCompatibilityVariable(actualVolume, baseVolume, staticVolume); - } - - cvVolume[s].put(uc, UtilizationClassVariable.CLOSE_UTIL_VOL, LayerType.PRIMARY, adjustment); - } - - int primarySpeciesVolumeGroup = lps.getVolumeEquationGroups()[s]; - float primarySpeciesQMDAll = bank.quadMeanDiameters[s][UC_ALL_INDEX]; - var wholeStemVolume = bank.treesPerHectare[s][UC_ALL_INDEX] * fps.getEstimators() - .estimateWholeStemVolumePerTree(primarySpeciesVolumeGroup, spLoreyHeight_All, primarySpeciesQMDAll); - - wholeStemVolumes.setCoe(UC_ALL_INDEX, wholeStemVolume); - - fps.getEstimators().estimateWholeStemVolume( - UtilizationClass.ALL, 0.0f, primarySpeciesVolumeGroup, spLoreyHeight_All, quadMeanDiameters, - basalAreas, wholeStemVolumes - ); - - for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - float adjustment = 0.0f; - float basalArea = basalAreas.getCoe(uc.index); - if (growthDetails.allowCalculation(basalArea, B_BASE_MIN, (l, r) -> l > r)) { - adjustment = calculateWholeStemVolume( - bank.wholeStemVolumes[s][uc.ordinal()], basalArea, wholeStemVolumes.getCoe(uc.index) - ); - } - - cvVolume[s].put(uc, UtilizationClassVariable.WHOLE_STEM_VOL, LayerType.PRIMARY, adjustment); - } - - fps.getEstimators().estimateQuadMeanDiameterByUtilization(lps.getBecZone(), quadMeanDiameters, genusName); - - fps.getEstimators() - .estimateBaseAreaByUtilization(lps.getBecZone(), quadMeanDiameters, basalAreas, genusName); - - // Calculate trees-per-hectare per utilization - treesPerHectare.setCoe(UtilizationClass.ALL.index, bank.treesPerHectare[s][UC_ALL_INDEX]); - for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - treesPerHectare.setCoe( - uc.index, - BaseAreaTreeDensityDiameter - .treesPerHectare(basalAreas.getCoe(uc.index), quadMeanDiameters.getCoe(uc.index)) - ); - } - - ReconcilationMethods.reconcileComponents(basalAreas, treesPerHectare, quadMeanDiameters); - - for (UtilizationClass uc : UtilizationClass.UTIL_CLASSES) { - float baCvValue = bank.basalAreas[s][uc.ordinal()] - basalAreas.getCoe(uc.index); - cvBasalArea[s].put(uc, LayerType.PRIMARY, baCvValue); - - float originalQmd = bank.quadMeanDiameters[s][uc.ordinal()]; - float adjustedQmd = quadMeanDiameters.getCoe(uc.index); - - float qmdCvValue; - if (growthDetails.allowCalculation(() -> originalQmd < B_BASE_MIN)) { - qmdCvValue = 0.0f; - } else if (originalQmd > 0 && adjustedQmd > 0) { - qmdCvValue = originalQmd - adjustedQmd; - } else { - qmdCvValue = 0.0f; - } - - cvQuadraticMeanDiameter[s].put(uc, LayerType.PRIMARY, qmdCvValue); - } - - // Small components - - cvSmall[s] = calculateSmallCompatibilityVariables(s, growthDetails); - } - - lps.setCompatibilityVariableDetails(cvVolume, cvBasalArea, cvQuadraticMeanDiameter, cvSmall); - } - /** * Function that calculates values for the small component compatibility variables and returns the result. * @@ -2626,11 +2409,11 @@ void setCompatibilityVariables() throws ProcessingException { * * @throws ProcessingException */ - private HashMap + public HashMap calculateSmallCompatibilityVariables(int speciesIndex, ForwardControlVariables forwardControlVariables) throws ProcessingException { - var lps = fps.getPrimaryLayerProcessingState(); + LayerProcessingState lps = fps.getPrimaryLayerProcessingState(); Bank bank = lps.getBank(); Region region = lps.getBecZone().getRegion(); @@ -2669,7 +2452,9 @@ void setCompatibilityVariables() throws ProcessingException { float spInputBasalArea_Small = bank.basalAreas[speciesIndex][UC_SMALL_INDEX]; spCvSmall.put(UtilizationClassVariable.BASAL_AREA, spInputBasalArea_Small - spBaSmall); - if (forwardControlVariables.allowCalculation(spInputBasalArea_Small, B_BASE_MIN, (l, r) -> l > r)) { + if (forwardControlVariables.allowCalculation( + spInputBasalArea_Small, ProcessingEngine.B_BASE_MIN, (l, r) -> l > r + )) { float spInputQuadMeanDiameter_Small = bank.quadMeanDiameters[speciesIndex][UC_SMALL_INDEX]; spCvSmall.put(UtilizationClassVariable.QUAD_MEAN_DIAMETER, spInputQuadMeanDiameter_Small - spDqSmall); } else { @@ -2686,7 +2471,9 @@ void setCompatibilityVariables() throws ProcessingException { float spInputWholeStemVolume_Small = bank.wholeStemVolumes[speciesIndex][UC_SMALL_INDEX]; if (spInputWholeStemVolume_Small > 0.0f && spMeanVolumeSmall > 0.0f - && forwardControlVariables.allowCalculation(spInputBasalArea_Small, B_BASE_MIN, (l, r) -> l >= r)) { + && forwardControlVariables.allowCalculation( + spInputBasalArea_Small, ProcessingEngine.B_BASE_MIN, (l, r) -> l >= r + )) { float spInputTreePerHectare_Small = bank.treesPerHectare[speciesIndex][UC_SMALL_INDEX]; @@ -2811,32 +2598,7 @@ private float meanVolumeSmall(String speciesName, float spHlSmall, float spDqSma return exp(a0 + a1 * log(spDqSmall) + a2 * log(spHlSmall) + a3 * spDqSmall); } - private static float calculateCompatibilityVariable(float actualVolume, float baseVolume, float staticVolume) { - - float staticRatio = staticVolume / baseVolume; - float staticLogit; - if (staticRatio <= 0.0f) { - staticLogit = -7.0f; - } else if (staticRatio >= 1.0f) { - staticLogit = 7.0f; - } else { - staticLogit = clamp(log(staticRatio / (1.0f - staticRatio)), -7.0f, 7.0f); - } - - float actualRatio = actualVolume / baseVolume; - float actualLogit; - if (actualRatio <= 0.0f) { - actualLogit = -7.0f; - } else if (actualRatio >= 1.0f) { - actualLogit = 7.0f; - } else { - actualLogit = clamp(log(actualRatio / (1.0f - actualRatio)), -7.0f, 7.0f); - } - - return actualLogit - staticLogit; - } - - private static float calculateWholeStemVolume(float actualVolume, float basalArea, float staticVolume) { + public static float calculateWholeStemVolume(float actualVolume, float basalArea, float staticVolume) { float staticRatio = staticVolume / basalArea; float staticLogit; @@ -3156,7 +2918,7 @@ static void estimateMissingSiteIndices(ForwardLayerProcessingState lps) throws P * * @param state the bank in which the calculations are performed */ - static void calculateCoverages(ForwardLayerProcessingState lps) { + static void calculateCoverages(LayerProcessingState lps) { Bank bank = lps.getBank(); @@ -3561,4 +3323,9 @@ protected ForwardExecutionStep getFirstStep() { protected ForwardExecutionStep getLastStep() { return ForwardExecutionStep.ALL; } + + @Override + protected ForwardProcessingState getState(Map controlMap) throws ProcessingException { + return new ForwardProcessingState(controlMap); + } } diff --git a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java index e93293599..77f79f3a6 100644 --- a/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java +++ b/lib/vdyp-forward/src/main/java/ca/bc/gov/nrs/vdyp/forward/ForwardProcessingState.java @@ -7,6 +7,7 @@ import ca.bc.gov.nrs.vdyp.forward.controlmap.ForwardResolvedControlMapImpl; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.LayerProcessingState; import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; public class ForwardProcessingState extends ProcessingState { @@ -21,7 +22,7 @@ protected ForwardResolvedControlMap resolveControlMap(Map contro } @Override - protected ForwardLayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) + protected LayerProcessingState createLayerState(VdypPolygon polygon, VdypLayer layer) throws ProcessingException { return new ForwardLayerProcessingState(this, polygon, layer); } diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java index 81d02dcc2..652f62bda 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow10StoreSpeciesDetails.java @@ -12,9 +12,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -57,7 +59,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_13_STORE_SPECIES_DETAILS); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_13_STORE_SPECIES_DETAILS); // VDYP7 reports [], -9, -9, 35.473381, -9, -9) Bank bank = fpe.fps.getPrimaryLayerProcessingState().getBank(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java index e4ccd77e9..158eca363 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow11UpdateCompatibilityVariables.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -55,7 +57,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_10_COMPATIBILITY_VARS); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_10_COMPATIBILITY_VARS); // VDYP7 reports [], -9, -9, 35.473381, -9, -9) ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java index 043f74bb9..0d94596cc 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow1CalculateDominantHeightDeltaTest.java @@ -25,8 +25,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -115,7 +117,7 @@ void testCurveExtension2() throws ProcessingException { fpe.fps.setPolygon(polygon); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_1_LAYER_DHDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_1_LAYER_DHDELTA.predecessor()); float hd = 26.5f; int sc = 11; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java index e62a30c32..17a84347d 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow2CalculateBasalAreaDeltaTest.java @@ -12,8 +12,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -55,7 +57,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 54.0f; float hd = 35.2999992f; @@ -75,7 +77,7 @@ void testYoungPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 30.0f; float hd = 10.0f; @@ -95,7 +97,7 @@ void testDebugSettings3EqualsZeroPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_2_LAYER_BADELTA.predecessor()); float yabh = 54.0f; float hd = 35.2999992f; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java index ff01ca7e8..30c1bec9a 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow3CalculateQuadMeanDiameterDeltaTest.java @@ -13,9 +13,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; import ca.bc.gov.nrs.vdyp.common.Reference; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; @@ -58,7 +60,7 @@ void testMixedModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); @@ -89,7 +91,7 @@ void testFiatOnlyModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); /* this value will force the fiat only calculations. */ @@ -120,7 +122,7 @@ void testEmpiricalOnlyModel() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 1); /* this value will force the empirical only calculations. */ @@ -151,7 +153,7 @@ void testMixedModelWithInterpolation() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 2); @@ -182,7 +184,7 @@ void testMinimumApplied() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); @@ -213,7 +215,7 @@ void testLimitApplied() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_3_LAYER_DQDELTA.predecessor()); ForwardDebugSettings debugSettings = fpe.fps.getControlMap().getDebugSettings(); debugSettings.setValue(Vars.DQ_GROWTH_MODEL_6, 0); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java index 700f38546..d564ea6c7 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow4CalculateLoreyHeightEstimatesTest.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -55,7 +57,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; @@ -83,7 +85,7 @@ void testDebug8Setting2Path() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float dhStart = 35.3f; diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java index c161a88f7..822a3791e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow5SpeciesBaDqTphTest.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -55,7 +57,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_5A_LH_EST); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_5A_LH_EST); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baStart = 45.3864441f; @@ -108,7 +110,7 @@ void testGrowUsingNoSpeciesDynamics() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); float baChangeRate = 0.00775236264f; @@ -150,7 +152,7 @@ void testGrowUsingFullSpeciesDynamics() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_4_LAYER_BA_AND_DQTPH_EST); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); fpe.fps.getControlMap().getDebugSettings().setValue(Vars.SPECIES_DYNAMICS_1, 0); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java index e9f5a7989..ad718f053 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow6TreesPerHectareTest.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -54,7 +56,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_6_LAYER_TPH2); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_6_LAYER_TPH2); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java index 77bf8a4b6..d31716df6 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow7QuadMeanDiameterTest.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -54,7 +56,7 @@ void testStandardPath() throws ProcessingException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_7_LAYER_DQ2); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_7_LAYER_DQ2); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java index dbdc9c0ec..48f99e157 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow8PerSpeciesLoreyHeightTest.java @@ -11,8 +11,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.model.ForwardDebugSettings.Vars; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -56,7 +58,7 @@ void testStandardPath() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.fps.getControlMap().getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 0); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); @@ -75,7 +77,7 @@ void testDebug8Setting2() throws ProcessingException { VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); fpe.fps.getControlMap().getDebugSettings().setValue(Vars.LOREY_HEIGHT_CHANGE_STRATEGY_8, 2); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_8_SPECIES_LH); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java index 9e013d3c5..dbce9d26e 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/Grow9PercentagesOfForestedLand.java @@ -12,8 +12,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; import ca.bc.gov.nrs.vdyp.common.ControlKey; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.forward.test.ForwardTestUtils; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.streaming.StreamingParser; @@ -55,7 +57,7 @@ void testStandardPath() throws ProcessingException, ValueParseException { // Select the first polygon - 01002 S000001 00(1970) VdypPolygon polygon = forwardDataStreamReader.readNextPolygon().orElseThrow(); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.GROW_9_SPECIES_PCT); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.GROW_9_SPECIES_PCT); ForwardLayerProcessingState lps = fpe.fps.getPrimaryLayerProcessingState(); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java index e966d8a9a..dfde2a3c2 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminaryForwardProcessingEngineStepsTest.java @@ -21,12 +21,14 @@ import ca.bc.gov.nrs.vdyp.common_calculators.custom_exceptions.NoAnswerException; import ca.bc.gov.nrs.vdyp.common_calculators.custom_exceptions.SpeciesErrorException; import ca.bc.gov.nrs.vdyp.common_calculators.enumerations.SiteIndexEquation; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.model.CommonData; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.VdypEntity; import ca.bc.gov.nrs.vdyp.si32.site.SiteTool; +import ca.bc.gov.nrs.vdyp.test.TestUtils; class PreliminaryForwardProcessingEngineStepsTest extends AbstractForwardProcessingEngineTest { @@ -130,7 +132,7 @@ void testGroupAndStratumNumberSpecialCases() throws IOException, ResourceParseEx var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.DETERMINE_POLYGON_RANKINGS); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.DETERMINE_POLYGON_RANKINGS); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesIndex(), is(1)); assertThrows( @@ -160,7 +162,7 @@ void testCalculateMissingSiteCurves() throws IOException, ResourceParseException var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -196,7 +198,7 @@ void testCalculateMissingSiteCurvesNoSiteCurveData() var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.CALCULATE_MISSING_SITE_CURVES); // Cannot check 0 since determinePolygonRankings has not been executed. assertThat(fpe.fps.getPrimaryLayerProcessingState().getSiteCurveNumber(1), is(118)); @@ -236,7 +238,7 @@ void testEstimateMissingSiteIndicesStep1() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); // Despite 13.40 being in the data stream, the change (2024/8/29) to ignore site information // for all species of the layer except the primary means that method (1) will never be @@ -283,7 +285,7 @@ void testEstimateMissingSiteIndicesStep2() throws ProcessingException, IOExcepti var polygon = reader.readNextPolygon().orElseThrow(); ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); + fpe.processPolygon(polygon, ProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_SITE_INDICES); var sourceSiteCurve = SiteIndexEquation.SI_CWC_BARKER; var sourceSiteIndex = 13.4f; @@ -315,7 +317,7 @@ void testEstimateMissingYearsToBreastHeightValues() ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); fpe.processPolygon( - polygon, ForwardProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES + polygon, ProcessingEngine.ForwardExecutionStep.ESTIMATE_MISSING_YEARS_TO_BREAST_HEIGHT_VALUES ); assertThat( @@ -351,7 +353,7 @@ void testCalculateDominantHeightAgeSiteIndex() throws ProcessingException, IOExc assertThrows( ProcessingException.class, () -> fpe.processPolygon( - polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + polygon, ProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX ) ); } @@ -390,9 +392,11 @@ void testCalculateDominantHeightAgeSiteIndexNoSecondary() ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); fpe.processPolygon( - polygon, ForwardProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + polygon, ProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX ); + TestUtils.writeModel(fpe.fps, System.out, 3, "initialState"); + assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesDominantHeight(), is(22.950302f)); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesSiteIndex(), is(34.0f)); assertThat(fpe.fps.getPrimaryLayerProcessingState().getPrimarySpeciesTotalAge(), is(22.0f)); diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java index 9007e5e42..1a4bdbeda 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/PreliminarySetCompatibilityVariablesTest.java @@ -15,23 +15,398 @@ import static org.hamcrest.Matchers.is; import java.io.IOException; +import java.util.Optional; import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.VdypEntity; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.processing_state.ProcessingState; +import ca.bc.gov.nrs.vdyp.processing_state.TestProcessingState; +import ca.bc.gov.nrs.vdyp.test.TestUtils; class PreliminarySetCompatibilityVariablesTest extends AbstractForwardProcessingEngineTest { @SuppressWarnings("unused") private static final Logger logger = LoggerFactory.getLogger(PreliminarySetCompatibilityVariablesTest.class); + ProcessingState initialState; + + @BeforeEach + void initialState() throws ProcessingException { + /* the following ProcessingState definition was generated */ + + /* the following Polygon definition was generated */ + + var polygon = VdypPolygon.build(pb -> { + pb.polygonIdentifier("01002 S000001 00", 1970); + + pb.biogeoclimaticZone(Utils.getBec("CWH", controlMap)); + pb.forestInventoryZone("A"); + + pb.inventoryTypeGroup(Optional.of(37)); + pb.targetYear(Optional.of(1990)); + + pb.mode(Optional.of(ca.bc.gov.nrs.vdyp.model.PolygonMode.START)); + pb.percentAvailable(99.000000f); + + pb.addLayer(lb -> { + lb.layerType(ca.bc.gov.nrs.vdyp.model.LayerType.PRIMARY); + + lb.empiricalRelationshipParameterIndex(Optional.empty()); + + lb.inventoryTypeGroup(Optional.of(37)); + + lb.loreyHeight(Utils.heightVector(7.016600f, 30.972401f)); + lb.treesPerHectare( + Utils.utilizationVector( + 5.292929f, 601.333313f, 65.474747f, 72.656563f, 74.343430f, 388.868683f + ) /* ALL does not match sum of bands */ + ); + lb.quadMeanDiameter( + Utils.utilizationVector( + 6.063298f, 30.999918f, 10.212870f, 15.043846f, 20.077644f, 36.730598f + ) /* ALL does not match sum of bands */ + ); + lb.baseArea( + Utils.utilizationVector( + 0.015283f, 45.386452f, 0.536364f, 1.291465f, 2.353737f, 41.204899f + ) /* ALL does not match sum of bands */ + ); + + lb.wholeStemVolume( + Utils.utilizationVector( + 0.063636f, 627.249939f, 2.624141f, 9.197676f, 22.628180f, 592.799988f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 0.000000f, 598.184082f, 0.387273f, 6.994444f, 20.327675f, 570.474609f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 0.000000f, 586.028320f, 0.383232f, 6.916060f, 20.089291f, 558.639771f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.000000f, 583.457458f, 0.382626f, 6.901413f, 20.037878f, 556.135437f + ) /* ALL does not match sum of bands */ + ); + lb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0.000000f, 0.365960f, 6.604445f, 19.146969f, 528.444153f) + ); + + lb.addSpecies(sb -> { + sb.genus("B"); + sb.genus(3); + + sb.breakageGroup(5); + sb.volumeGroup(12); + sb.decayGroup(7); + + sb.percentGenus(0.008967f); + + sb.addSp64Distribution("B", 100.000000f); + + sb.loreyHeight(Utils.heightVector(8.027200f, 36.755299f)); + sb.treesPerHectare( + Utils.utilizationVector( + 0.000000f, 5.212121f, 0.767677f, 0.939394f, 0.888889f, 2.626262f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 6.100000f, 31.531136f, 9.170650f, 13.660340f, 18.178658f, 42.070770f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea(Utils.utilizationVector(0.000000f, 0.005071f, 0.013768f, 0.023071f, 0.365081f)); + + sb.wholeStemVolume(Utils.utilizationVector(0.000000f, 0.018687f, 0.076465f, 0.176566f, 6.000808f)); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector(0.000000f, 0.000909f, 0.050303f, 0.153636f, 5.814545f) + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 0.000000f, 5.905555f, 0.000909f, 0.050202f, 0.152929f, 5.701616f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0.000000f, 0.000909f, 0.050101f, 0.152727f, 5.671313f) + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.000000f, 5.573434f, 0.000909f, 0.047980f, 0.145960f, 5.378687f + ) /* ALL does not match sum of bands */ + ); + + }); + lb.addSpecies(sb -> { + sb.genus("C"); + sb.genus(4); + + sb.breakageGroup(6); + sb.volumeGroup(20); + sb.decayGroup(14); + + sb.percentGenus(0.112301f); + + sb.addSp64Distribution("C", 100.000000f); + + sb.loreyHeight(Utils.heightVector(6.460200f, 22.958401f)); + sb.treesPerHectare( + Utils.utilizationVector( + 4.444444f, 84.303024f, 16.282829f, 18.050505f, 16.717171f, 33.252522f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 5.997417f, 27.745222f, 10.063532f, 14.862596f, 19.873747f, 39.793953f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 0.012556f, 5.096939f, 0.129515f, 0.313162f, 0.518576f, 4.135696f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 0.051212f, 43.907677f, 0.608788f, 1.943131f, 3.861616f, 37.494141f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector(0.000000f, 0.112727f, 1.424545f, 3.349697f, 34.951412f) + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0.000000f, 0.110505f, 1.384848f, 3.245151f, 31.889189f) + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.000000f, 35.649494f, 0.110101f, 1.376566f, 3.218081f, 30.944645f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.000000f, 33.246864f, 0.104545f, 1.304545f, 3.037980f, 28.799898f + ) /* ALL does not match sum of bands */ + ); + + }); + lb.addSpecies(sb -> { + sb.genus("D"); + sb.genus(5); + + sb.breakageGroup(12); + sb.volumeGroup(25); + sb.decayGroup(19); + + sb.percentGenus(0.652143f); + + sb.addSp64Distribution("D", 100.000000f); + + sb.loreyHeight(Utils.heightVector(10.603300f, 33.743999f)); + sb.treesPerHectare( + Utils.utilizationVector( + 0.474747f, 290.606049f, 1.656566f, 2.717172f, 13.959595f, 272.282806f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 6.479955f, 36.011185f, 10.470093f, 15.579478f, 20.527220f, 36.869789f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 0.001566f, 29.598473f, 0.014263f, 0.051798f, 0.461980f, 29.070423f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 0.007879f, 464.164917f, 0.110202f, 0.565859f, 6.073636f, 457.415314f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector( + 0.000000f, 448.570099f, 0.057677f, 0.509899f, 5.698383f, 442.304016f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 0.000000f, 440.937378f, 0.057172f, 0.505758f, 5.654040f, 434.720367f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.000000f, 439.678558f, 0.057071f, 0.505556f, 5.651313f, 433.464630f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0.000000f, 0.054646f, 0.483131f, 5.392222f, 411.943604f) + ); + + sb.addSite(ib -> { + ib.ageTotal(Optional.of(55.000000f)); + ib.height(Optional.of(35.299999f)); + ib.siteCurveNumber(Optional.of(13)); + ib.siteIndex(Optional.of(35.000000f)); + ib.yearsToBreastHeight(Optional.of(1.000000f)); + }); + + }); + lb.addSpecies(sb -> { + sb.genus("H"); + sb.genus(8); + + sb.breakageGroup(17); + sb.volumeGroup(37); + sb.decayGroup(31); + + sb.percentGenus(0.129306f); + + sb.addSp64Distribution("H", 100.000000f); + + sb.loreyHeight(Utils.heightVector(7.546400f, 22.770399f)); + sb.treesPerHectare( + Utils.utilizationVector( + 0.000000f, 169.595947f, 44.010098f, 46.454544f, 34.272724f, 44.868683f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + Float.NaN, 20.990366f, 10.276456f, 15.108315f, 20.090958f, 31.892609f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea(Utils.utilizationVector(0.000000f, 0.365030f, 0.832818f, 1.086525f, 3.584374f)); + + sb.wholeStemVolume(Utils.utilizationVector(0.000000f, 1.756060f, 5.925858f, 9.749595f, 39.020805f)); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector(0.000000f, 0.194444f, 4.460101f, 8.661818f, 37.016060f) + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector( + 0.000000f, 49.569897f, 0.193232f, 4.428889f, 8.587777f, 36.359898f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0.000000f, 0.193030f, 4.423131f, 8.568384f, 36.163937f) + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0.000000f, 0.185253f, 4.244747f, 8.221919f, 34.502422f) + ); + + }); + lb.addSpecies(sb -> { + sb.genus("S"); + sb.genus(15); + + sb.breakageGroup(28); + sb.volumeGroup(66); + sb.decayGroup(54); + + sb.percentGenus(0.097282f); + + sb.addSp64Distribution("S", 100.000000f); + + sb.loreyHeight(Utils.heightVector(8.200300f, 32.012501f)); + sb.treesPerHectare( + Utils.utilizationVector( + 0.363636f, 51.616158f, 2.757576f, 4.505050f, 8.515151f, 35.848484f + ) /* ALL does not match sum of bands */ + ); + sb.quadMeanDiameter( + Utils.utilizationVector( + 6.377533f, 33.002167f, 10.186822f, 15.028074f, 19.852715f, 37.923717f + ) /* ALL does not match sum of bands */ + ); + sb.baseArea( + Utils.utilizationVector( + 0.001162f, 4.415303f, 0.022475f, 0.079909f, 0.263586f, 4.049323f + ) /* ALL does not match sum of bands */ + ); + + sb.wholeStemVolume( + Utils.utilizationVector( + 0.004545f, 56.452423f, 0.130404f, 0.686364f, 2.766768f, 52.868885f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeByUtilization( + Utils.utilizationVector(0.000000f, 0.021515f, 0.549495f, 2.464242f, 50.388485f) + ); + sb.closeUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0.000000f, 0.021414f, 0.546465f, 2.449495f, 49.968582f) + ); + sb.closeUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector( + 0.000000f, 52.905857f, 0.021414f, 0.546061f, 2.447374f, 49.890903f + ) /* ALL does not match sum of bands */ + ); + sb.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector( + 0.000000f, 50.713131f, 0.020606f, 0.524141f, 2.348889f, 47.819595f + ) /* ALL does not match sum of bands */ + ); + + }); + + lb.primaryGenus(Optional.of("D")); + }); + }); + + /* End of generated Polygon definition */ + initialState = new TestProcessingState(controlMap); + initialState.setPolygon(polygon); + var primaryBank = initialState.getPrimaryLayerProcessingState().getBank(); + System.arraycopy( + new float[] { 0.000000f, Float.NaN, Float.NaN, 55.000000f, Float.NaN, Float.NaN }, 0, + primaryBank.ageTotals, 0, 6 + ); + System.arraycopy( + new float[] { 0.000000f, 0.896721f, 11.230113f, 65.214325f, 12.930618f, 9.728219f }, 0, + primaryBank.percentagesOfForestedLand, 0, 6 + ); + System.arraycopy( + new float[][] { { 0.015283f, 45.386444f, 0.536354f, 1.291455f, 2.353737f, 41.204899f }, { 0.000000f, + 0.406990f, 0.005071f, 0.013768f, 0.023071f, 0.365081f }, { 0.012556f, 5.096949f, 0.129515f, + 0.313162f, 0.518576f, 4.135696f }, { 0.001566f, 29.598463f, 0.014263f, 0.051798f, + 0.461980f, 29.070423f }, { 0.000000f, 5.868748f, 0.365030f, 0.832818f, + 1.086525f, 3.584374f }, { 0.001162f, 4.415293f, 0.022475f, 0.079909f, + 0.263586f, 4.049323f } }, 0, primaryBank.basalAreas, 0, 6 + ); + System.arraycopy(new String[] { null, "B", "C", "D", "H", "S" }, 0, primaryBank.speciesNames, 0, 6); + System.arraycopy( + new float[] { 35.000000f, Float.NaN, Float.NaN, 35.000000f, Float.NaN, Float.NaN }, 0, + primaryBank.siteIndices, 0, 6 + ); + System.arraycopy( + new float[] { 0.000000f, Float.NaN, Float.NaN, 35.299999f, Float.NaN, Float.NaN }, 0, + primaryBank.dominantHeights, 0, 6 + ); + System.arraycopy( + new float[] { 0.000000f, Float.NaN, Float.NaN, 54.000000f, Float.NaN, Float.NaN }, 0, + primaryBank.yearsAtBreastHeight, 0, 6 + ); + System.arraycopy( + new float[] { 0.000000f, 5.000000f, 7.500000f, 1.000000f, 4.500000f, 5.200000f }, 0, + primaryBank.yearsToBreastHeight, 0, 6 + ); + System.arraycopy(new int[] { 0, 118, 122, 13, 99, 59 }, 0, primaryBank.siteCurveNumbers, 0, 6); + + /* End of generated ProcessingState definition */ + } + @Test /** SET_COMPATIBILITY_VARIABLES */ void testSetCompatibilityVariables() throws ResourceParseException, IOException, ProcessingException { @@ -39,9 +414,36 @@ void testSetCompatibilityVariables() throws ResourceParseException, IOException, var reader = new ForwardDataStreamReader(controlMap); var polygon = reader.readNextPolygon().orElseThrow(); - ForwardProcessingEngine fpe = new ForwardProcessingEngine(controlMap); - fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.SET_COMPATIBILITY_VARIABLES); + ProcessingEngine fpe = new ProcessingEngine(controlMap) { + + @Override + public void processPolygon(VdypPolygon polygon, ExecutionStep lastStepInclusive) + throws ProcessingException { + // TODO Auto-generated method stub + + } + @Override + protected ExecutionStep getFirstStep() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected ExecutionStep getLastStep() { + // TODO Auto-generated method stub + return null; + } + + }; + fpe.fps = initialState; + fpe. + //fpe.processPolygon(polygon, ForwardProcessingEngine.ForwardExecutionStep.SET_COMPATIBILITY_VARIABLES); + + fpe.processPolygon( + polygon, ProcessingEngine.ForwardExecutionStep.CALCULATE_DOMINANT_HEIGHT_AGE_SITE_INDEX + ); + TestUtils.writeModel(fpe.fps, System.out, 3, "initialState"); // These values have been verified against the FORTRAN implementation, allowing for minor // platform-specific differences. diff --git a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java index b6deedbaf..5850293a7 100644 --- a/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java +++ b/lib/vdyp-forward/src/test/java/ca/bc/gov/nrs/vdyp/forward/ProcessPolygonBasicTest.java @@ -11,6 +11,8 @@ import org.slf4j.LoggerFactory; import ca.bc.gov.nrs.vdyp.application.ProcessingException; +import ca.bc.gov.nrs.vdyp.forward.ForwardProcessingEngine.ForwardExecutionStep; +import ca.bc.gov.nrs.vdyp.application.ProcessingEngine; import ca.bc.gov.nrs.vdyp.application.ProcessingEngine.ExecutionStep; import ca.bc.gov.nrs.vdyp.io.parse.common.ResourceParseException; @@ -32,7 +34,7 @@ void testOnePolygon() throws IOException, ResourceParseException, ProcessingExce var polygon = forwardDataStreamReader.readNextPolygon(); if (polygon.isPresent()) { - fpe.processPolygon(polygon.get(), ForwardProcessingEngine.ForwardExecutionStep.GROW); + fpe.processPolygon(polygon.get(), ProcessingEngine.ForwardExecutionStep.GROW); nPolygonsProcessed += 1; }