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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ jobs:
- name: Fetch Sources
uses: actions/checkout@v3.5.1

# Validate wrapper
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1.0.6
# Setup Gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

# Setup Java 17 environment for the next steps
- name: Setup Java
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ jobs:
- name: Fetch Sources
uses: actions/checkout@v3.5.1

# Validate wrapper
- name: Gradle Wrapper Validation
uses: gradle/wrapper-validation-action@v1.0.6
# Setup Gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

# Setup Java 17 environment for the next steps
- name: Setup Java
Expand Down
8 changes: 4 additions & 4 deletions cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package ch.kleis.lcaac.cli.cmd

import ch.kleis.lcaac.cli.csv.CsvProcessor
import ch.kleis.lcaac.cli.csv.assess.AssessCsvProcessor
import ch.kleis.lcaac.cli.csv.CsvRequest
import ch.kleis.lcaac.cli.csv.CsvRequestReader
import ch.kleis.lcaac.cli.csv.CsvResultWriter
import ch.kleis.lcaac.cli.csv.assess.AssessCsvResultWriter
import ch.kleis.lcaac.core.config.LcaacConfig
import ch.kleis.lcaac.core.math.basic.BasicOperations
import ch.kleis.lcaac.grammar.Loader
Expand Down Expand Up @@ -67,9 +67,9 @@ class AssessCommand : CliktCommand(name = "assess", help = "Returns the unitary
overriddenGlobals = dataExpressionMap(BasicOperations, globals),
).load(files, listOf(LoaderOption.WITH_PRELUDE))

val processor = CsvProcessor(yamlConfig, symbolTable, workingDirectory.path)
val processor = AssessCsvProcessor(yamlConfig, symbolTable, workingDirectory.path)
val iterator = loadRequests()
val writer = CsvResultWriter()
val writer = AssessCsvResultWriter()
var first = true
while (iterator.hasNext()) {
val request = iterator.next()
Expand Down
199 changes: 41 additions & 158 deletions cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TraceCommand.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
package ch.kleis.lcaac.cli.cmd

import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram
import ch.kleis.lcaac.cli.csv.CsvRequest
import ch.kleis.lcaac.cli.csv.CsvRequestReader
import ch.kleis.lcaac.cli.csv.trace.TraceCsvProcessor
import ch.kleis.lcaac.cli.csv.trace.TraceCsvResultWriter
import ch.kleis.lcaac.core.config.LcaacConfig
import ch.kleis.lcaac.core.datasource.ConnectorFactory
import ch.kleis.lcaac.core.datasource.DefaultDataSourceOperations
import ch.kleis.lcaac.core.datasource.csv.CsvConnectorBuilder
import ch.kleis.lcaac.core.lang.evaluator.Evaluator
import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException
import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer
import ch.kleis.lcaac.core.lang.value.FullyQualifiedSubstanceValue
import ch.kleis.lcaac.core.lang.value.IndicatorValue
import ch.kleis.lcaac.core.lang.value.PartiallyQualifiedSubstanceValue
import ch.kleis.lcaac.core.lang.value.ProductValue
import ch.kleis.lcaac.core.math.basic.BasicOperations
import ch.kleis.lcaac.core.math.basic.BasicOperations.toDouble
import ch.kleis.lcaac.core.prelude.Prelude.Companion.sanitize
import ch.kleis.lcaac.grammar.Loader
import ch.kleis.lcaac.grammar.LoaderOption
import com.charleskorn.kaml.decodeFromStream
Expand All @@ -26,8 +17,6 @@ import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.file
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVPrinter
import java.io.File

@Suppress("MemberVisibilityCanBePrivate", "DuplicatedCode")
Expand All @@ -45,6 +34,11 @@ class TraceCommand : CliktCommand(name = "trace", help = "Trace the contribution
.help("Path to project folder or yaml file.")
val projectPath: File by getProjectPath

val file: File? by option("-f", "--file").file(canBeDir = false)
.help("""
CSV file with parameter values.
Example: `lcaac trace <process name> -f params.csv`.
""".trimIndent())
val arguments: Map<String, String> by option("-D", "--parameter")
.help(
"""
Expand All @@ -67,159 +61,48 @@ class TraceCommand : CliktCommand(name = "trace", help = "Trace the contribution
}
else LcaacConfig()

val ops = BasicOperations
val files = lcaFiles(workingDirectory)
val symbolTable = Loader(
ops = BasicOperations,
overriddenGlobals = dataExpressionMap(BasicOperations, globals),
).load(files, listOf(LoaderOption.WITH_PRELUDE))

val factory = ConnectorFactory(
workingDirectory.path,
yamlConfig,
ops,
symbolTable,
listOf(CsvConnectorBuilder())
)
val sourceOps = DefaultDataSourceOperations(ops, yamlConfig, factory.buildConnectors())

val evaluator = Evaluator(symbolTable, ops, sourceOps)
val template = symbolTable.getTemplate(name, labels)
?: throw EvaluatorException("unknown template $name$labels")
val args = prepareArguments(
DataExpressionReducer(symbolTable.data, symbolTable.dataSources, ops, sourceOps),
template,
arguments,
)
val trace = evaluator.trace(template, args)
val system = trace.getSystemValue()
val entryPoint = trace.getEntryPoint()

val program = ContributionAnalysisProgram(system, entryPoint)
val analysis = program.run()

val observablePorts = analysis.getObservablePorts()
.getElements()
.sortedWith(trace.getComparator())
val controllablePorts = analysis.getControllablePorts().getElements()
.sortedBy { it.getShortName() }

val header = listOf(
"depth", "d_amount", "d_unit", "d_product", "alloc",
"name", "a", "b", "c", "amount", "unit",
).plus(
controllablePorts.flatMap {
listOf(
sanitize(it.getDisplayName()),
"${sanitize(it.getDisplayName())}_unit"
)
val processor = TraceCsvProcessor(yamlConfig, symbolTable, workingDirectory.path)
val iterator = loadRequests()
val writer = TraceCsvResultWriter()
var first = true
while (iterator.hasNext()) {
val request = iterator.next()
val result = processor.process(request)
if (first) {
echo(writer.header(result), trailingNewline = false)
first = false
}
writer.rows(result).forEach {
echo(it, trailingNewline = false)
}
)

val products = entryPoint.products.asSequence()
val lines = products.flatMap { demandedProduct ->
val demandedAmount = demandedProduct.quantity.amount
val demandedUnit = demandedProduct.quantity.unit
val demandedProductName = demandedProduct.product.name
val allocationAmount = (demandedProduct.allocation?.amount?.toDouble()
?: 1.0) * (demandedProduct.allocation?.unit?.scale ?: 1.0)
observablePorts.asSequence()
.map { row ->
val supply = analysis.supplyOf(row)
val depth = trace.getDepthOf(row)?.toString() ?: ""
val supplyAmount = supply.amount.value * allocationAmount
val prefix = when (row) {
is IndicatorValue -> {
listOf(
depth,
demandedAmount.toString(),
demandedUnit.toString(),
demandedProductName,
allocationAmount.toString(),
row.name,
"",
"",
"",
supplyAmount.toString(),
supply.unit.toString(),
)
}

is ProductValue -> {
listOf(
depth,
demandedAmount.toString(),
demandedUnit.toString(),
demandedProductName,
allocationAmount.toString(),
row.name,
row.fromProcessRef?.name ?: "",
row.fromProcessRef?.matchLabels?.toString() ?: "",
row.fromProcessRef?.arguments?.toString() ?: "",
supplyAmount.toString(),
supply.unit.toString(),
)
}

is FullyQualifiedSubstanceValue -> {
listOf(
depth,
demandedAmount.toString(),
demandedUnit.toString(),
demandedProductName,
allocationAmount.toString(),
row.name,
row.compartment,
row.subcompartment ?: "",
row.type.toString(),
supplyAmount.toString(),
supply.unit.toString(),
)
}

is PartiallyQualifiedSubstanceValue -> {
listOf(
depth,
demandedAmount.toString(),
demandedUnit.toString(),
demandedProductName,
allocationAmount.toString(),
row.name,
"",
"",
"",
supplyAmount.toString(),
supply.unit.toString(),
)
}
}
val impacts = controllablePorts.flatMap { col ->
val impact = analysis.getPortContribution(row, col)
val impactAmount = impact.amount.value * allocationAmount
listOf(
impactAmount.toString(),
impact.unit.toString(),
)
}
prefix.plus(impacts)
}
}
}

private fun loadRequests(): Iterator<CsvRequest> {
return file?.let { loadRequestsFrom(it) }
?: listOf(defaultRequest()).iterator()
}

val s = StringBuilder()
CSVPrinter(s, format).printRecord(header)
echo(s.toString(), trailingNewline = false)
lines
.forEach {
s.clear()
CSVPrinter(s, format).printRecord(it)
echo(s.toString(), trailingNewline = false)
}
private fun loadRequestsFrom(file: File): Iterator<CsvRequest> {
val reader = CsvRequestReader(name, labels, file.inputStream(), arguments)
return reader.iterator()
}

private val format = CSVFormat.DEFAULT.builder()
.setHeader()
.setSkipHeaderRecord(true)
.setRecordSeparator(System.lineSeparator())
.build()
private fun defaultRequest(): CsvRequest {
val pairs = arguments.toList()
val header = pairs.mapIndexed { index, pair -> pair.first to index }.toMap()
val record = pairs.map { it.second }
return CsvRequest(
name,
labels,
header,
record,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ch.kleis.lcaac.cli.csv
package ch.kleis.lcaac.cli.csv.assess

import ch.kleis.lcaac.cli.cmd.prepareArguments
import ch.kleis.lcaac.cli.csv.CsvRequest
import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram
import ch.kleis.lcaac.core.config.LcaacConfig
import ch.kleis.lcaac.core.datasource.ConnectorFactory
Expand All @@ -13,7 +14,7 @@ import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer
import ch.kleis.lcaac.core.math.basic.BasicNumber
import ch.kleis.lcaac.core.math.basic.BasicOperations

class CsvProcessor(
class AssessCsvProcessor(
config: LcaacConfig,
private val symbolTable: SymbolTable<BasicNumber>,
workingDirectory: String,
Expand All @@ -30,7 +31,7 @@ class CsvProcessor(
private val dataReducer = DataExpressionReducer(symbolTable.data, symbolTable.dataSources, ops, sourceOps)
private val evaluator = Evaluator(symbolTable, ops, sourceOps)

fun process(request: CsvRequest): List<CsvResult> {
fun process(request: CsvRequest): List<AssessCsvResult> {
val reqName = request.processName
val reqLabels = request.matchLabels
val template = symbolTable.getTemplate(reqName, reqLabels)
Expand All @@ -44,7 +45,7 @@ class CsvProcessor(
return entryPoint.products.map { output ->
val outputPort = output.product
val impacts = analysis.getUnitaryImpacts(outputPort)
CsvResult(
AssessCsvResult(
request,
outputPort,
impacts,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package ch.kleis.lcaac.cli.csv
package ch.kleis.lcaac.cli.csv.assess

import ch.kleis.lcaac.cli.csv.CsvRequest
import ch.kleis.lcaac.core.lang.value.MatrixColumnIndex
import ch.kleis.lcaac.core.lang.value.QuantityValue
import ch.kleis.lcaac.core.math.basic.BasicNumber

data class CsvResult(
data class AssessCsvResult(
val request: CsvRequest,
val output: MatrixColumnIndex<BasicNumber>,
val impacts: Map<MatrixColumnIndex<BasicNumber>, QuantityValue<BasicNumber>>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package ch.kleis.lcaac.cli.csv
package ch.kleis.lcaac.cli.csv.assess

import ch.kleis.lcaac.core.prelude.Prelude
import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVPrinter

class CsvResultWriter {
class AssessCsvResultWriter {
private val format = CSVFormat.DEFAULT.builder()
.setHeader()
.setSkipHeaderRecord(true)
.setRecordSeparator(System.lineSeparator())
.build()

fun header(first: CsvResult): String {
fun header(first: AssessCsvResult): String {
val header = first.request.columns()
.plus(listOf("product", "amount", "reference unit"))
.plus(first.impacts.toList()
Expand All @@ -26,7 +26,7 @@ class CsvResultWriter {
return s.toString()
}

fun row(result: CsvResult): String {
fun row(result: AssessCsvResult): String {
val line = result.request.arguments()
.plus(listOf(result.output.getShortName(), "1.0", result.output.referenceUnit().symbol))
.plus(result.impacts.toList()
Expand Down
Loading