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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/[email protected]

# Validate wrapper
- name: Gradle Wrapper Validation
uses: gradle/[email protected]
# 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/[email protected]

# Validate wrapper
- name: Gradle Wrapper Validation
uses: gradle/[email protected]
# Setup Gradle
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4

# Setup Java 17 environment for the next steps
- name: Setup Java
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ See our [book](https://lca-as-code.com/book) to learn more about the language.

From the source
```bash
git checkout v1.8.0
git checkout v1.8.1
./gradlew :cli:installDist
alias lcaac=$GIT_ROOT/cli/build/install/lcaac/bin/lcaac
lcaac version
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