Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.
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
4 changes: 2 additions & 2 deletions src/main/scala/sfc/agents/Banking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ object Banking:
def lendingRate(bank: BankState, cfg: Config, refRate: Rate)(using p: SimParams): Rate =
if bank.failed then refRate + Rate(FailedBankSpread)
else
val nplSpread = Math.min(NplSpreadCap, bank.nplRatio.toDouble * p.banking.nplSpreadFactor)
val nplSpread = Math.min(NplSpreadCap, (bank.nplRatio * p.banking.nplSpreadFactor).toDouble)
val carPenalty =
if bank.car.toDouble < p.banking.minCar.toDouble * CarPenaltyThreshMult then
Math.max(0.0, (p.banking.minCar.toDouble * CarPenaltyThreshMult - bank.car.toDouble) * CarPenaltyScale)
Expand Down Expand Up @@ -323,7 +323,7 @@ object Banking:
else
val projectedCar =
bank.capital.toDouble / (bank.loans.toDouble + bank.consumerLoans.toDouble + bank.corpBondHoldings.toDouble * CorpBondRiskWeight + amount.toDouble)
val approvalP = Math.max(MinApprovalProb, 1.0 - bank.nplRatio.toDouble * NplApprovalPenalty)
val approvalP = Math.max(MinApprovalProb, 1.0 - (bank.nplRatio * NplApprovalPenalty).toDouble)
val minCar = Macroprudential.effectiveMinCar(bank.id.toInt, ccyb.toDouble)
val carOk = projectedCar >= minCar
val lcrOk = if p.flags.bankLcr then bank.lcr.toDouble >= p.banking.lcrMin else true
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/sfc/agents/Firm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -480,11 +480,11 @@ object Firm:
allFirms: Vector[State],
)(using p: SimParams): (Double, Double) =
val localAuto = computeLocalAutoRatio(firm, allFirms)
val globalPanic = (w.real.automationRatio + w.real.hybridRatio * HybridPanicDiscount).toDouble * HybridPanicDiscount
val globalPanic = ((w.real.automationRatio + w.real.hybridRatio * HybridPanicDiscount) * HybridPanicDiscount).toDouble
val panic = localAuto * LocalPanicWeight + globalPanic * GlobalPanicWeight
val desper = if pnl.netAfterTax < PLN.Zero then DesperationBonus else 0.0
val strat =
if !fullAi.profitable && fullAi.canPay && fullAi.ready && fullAi.bankOk then (firm.riskProfile * firm.digitalReadiness).toDouble * StrategicAdoptBase
if !fullAi.profitable && fullAi.canPay && fullAi.ready && fullAi.bankOk then (firm.riskProfile * firm.digitalReadiness * StrategicAdoptBase).toDouble
else 0.0

val baseDiscount = UncertaintyBase + UncertaintySlope * (w.month.toDouble / p.timeline.duration.toDouble)
Expand All @@ -506,7 +506,7 @@ object Firm:
* implementation failure.
*/
private def rollFullAiUpgrade(firm: State, pnl: PnL, ai: UpgradeCandidate, rng: Random): Decision =
val failRate = FullAiBaseFailRate + (Ratio.One - firm.digitalReadiness).toDouble * FullAiFailDrSens
val failRate = FullAiBaseFailRate + ((Ratio.One - firm.digitalReadiness) * FullAiFailDrSens).toDouble
if rng.nextDouble() < failRate then
Decision.UpgradeFailed(pnl, BankruptReason.AiImplFailure, ai.capex * FailCapexFrac, ai.loan * FailLoanFrac, ai.down * FailDownFrac)
else
Expand All @@ -517,7 +517,7 @@ object Firm:
* efficiency), or success (good efficiency).
*/
private def rollHybridUpgrade(firm: State, pnl: PnL, hyb: UpgradeCandidate, hWkrs: Int, rng: Random): Decision =
val failRate = HybridBaseFailRate + (Ratio.One - firm.digitalReadiness).toDouble * HybridFailDrSens
val failRate = HybridBaseFailRate + ((Ratio.One - firm.digitalReadiness) * HybridFailDrSens).toDouble
val ir = rng.nextDouble()
if ir < failRate * CatastrophicFailFrac then
Decision.UpgradeFailed(pnl, BankruptReason.HybridImplFailure, hyb.capex * FailCapexFrac, hyb.loan * FailLoanFrac, hyb.down * FailDownFrac)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/sfc/agents/Household.scala
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ object Household:
/** Compute 800+ social transfer (PIT-exempt, lump-sum per child ≤ 18). */
def computeSocialTransfer(numChildren: Int)(using p: SimParams): PLN =
if !p.flags.social800 || numChildren <= 0 then PLN.Zero
else PLN(numChildren.toDouble * p.fiscal.social800Rate.toDouble)
else p.fiscal.social800Rate * numChildren.toDouble

/** Unemployment benefit (zasilek): 1500 PLN m1-3, 1200 PLN m4-6, 0 after. */
def computeBenefit(monthsUnemployed: Int)(using p: SimParams): PLN =
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/sfc/agents/Jst.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object Jst:
// 2. CIT share: JST gets ~6.71% of CIT
val citRevenue = govTaxRevenue * p.fiscal.jstCitShare.toDouble
// 3. Property tax: fixed per firm per year
val propertyTax = PLN(nFirms.toDouble * p.fiscal.jstPropertyTax.toDouble / 12.0)
val propertyTax = p.fiscal.jstPropertyTax * nFirms.toDouble / 12.0
// 4. Subwencja oświatowa (education subvention): ~3% of GDP annually
val subvention = gdp * p.fiscal.jstSubventionShare.toDouble / 12.0
// 5. Dotacje celowe (targeted grants): ~1% of GDP annually
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/sfc/engine/markets/CorporateBondMarket.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ object CorporateBondMarket:
* Spread widens with system NPL (credit risk channel).
*/
def computeYield(govBondYield: Rate, nplRatio: Ratio)(using p: SimParams): Rate =
val cyclicalSpread = p.corpBond.spread.toDouble * (1.0 + nplRatio.toDouble * NplSensitivity)
val spread = Math.min(MaxSpread, cyclicalSpread)
(govBondYield + Rate(spread)).max(Rate(MinYield))
val cyclicalSpread = p.corpBond.spread * (1.0 + (nplRatio * NplSensitivity).toDouble)
val spread = cyclicalSpread.min(Rate(MaxSpread))
(govBondYield + spread).max(Rate(MinYield))

/** @param total
* total monthly coupon across all holders
Expand Down
10 changes: 5 additions & 5 deletions src/main/scala/sfc/engine/mechanisms/FirmEntry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ object FirmEntry:

Firm.State(
id = slotId,
cash = PLN(p.firm.entryStartupCash.toDouble * sizeMult),
cash = p.firm.entryStartupCash * sizeMult,
debt = PLN.Zero,
tech = tech,
riskProfile = Ratio(rng.between(0.1, 0.9)),
Expand Down Expand Up @@ -149,20 +149,20 @@ object FirmEntry:
/** Initial physical capital stock from sector-specific capital-labor ratio.
*/
private def initCapitalStock(firmSize: Int, sector: Int)(using p: SimParams): PLN =
if p.flags.physCap then PLN(firmSize.toDouble * p.capital.klRatios.map(_.toDouble)(sector))
if p.flags.physCap then p.capital.klRatios(sector) * firmSize.toDouble
else PLN.Zero

/** Initial inventory from sector target ratio, scaled to firm capacity. */
private def initInventory(firmSize: Int, sector: Int)(using p: SimParams): PLN =
if p.flags.inventory then
val cap = p.firm.baseRevenue.toDouble * (firmSize.toDouble / p.pop.workersPerFirm) *
val cap = p.firm.baseRevenue * (firmSize.toDouble / p.pop.workersPerFirm) *
p.sectorDefs(sector).revenueMultiplier
PLN(cap * p.capital.inventoryTargetRatios.map(_.toDouble)(sector) * p.capital.inventoryInitRatio.toDouble)
cap * p.capital.inventoryTargetRatios(sector) * p.capital.inventoryInitRatio
else PLN.Zero

/** Initial green capital stock from sector-specific green K/L ratio. */
private def initGreenCapital(firmSize: Int, sector: Int)(using p: SimParams): PLN =
if p.flags.energy then PLN(firmSize.toDouble * p.climate.greenKLRatios.map(_.toDouble)(sector) * p.climate.greenInitRatio.toDouble)
if p.flags.energy then p.climate.greenKLRatios(sector) * firmSize.toDouble * p.climate.greenInitRatio
else PLN.Zero

private def pickSector(totalWeight: Double, sectorWeights: Vector[Double], rng: Random): Int =
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/sfc/engine/steps/PriceEquityStep.scala
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ object PriceEquityStep:
bankId = BankId(0),
equityRaised = PLN.Zero,
initialSize = newSize,
capitalStock = PLN(if p.flags.physCap then newSize.toDouble * p.capital.klRatios.map(_.toDouble)(sec.toInt) else 0.0),
capitalStock = if p.flags.physCap then p.capital.klRatios(sec.toInt) * newSize.toDouble else PLN.Zero,
bondDebt = PLN.Zero,
foreignOwned = false,
inventory = PLN.Zero,
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/sfc/init/EquityInit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ object EquityInit:
if p.flags.gpw then
val initHhEq =
if p.flags.gpwHhEquity
then PLN(totalPop * p.equity.hhEquityFrac.toDouble * Math.exp(p.household.savingsMu) * 0.05)
then PLN(totalPop.toDouble * Math.exp(p.household.savingsMu) * 0.05) * p.equity.hhEquityFrac
else PLN.Zero
EquityMarket.initial.copy(hhEquityWealth = initHhEq)
else EquityMarket.zero
16 changes: 8 additions & 8 deletions src/main/scala/sfc/init/FirmInit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ object FirmInit:
private def assignCapitalAndBank(firms: Vector[Firm.State], rng: Random)(using p: SimParams): Vector[Firm.State] =
firms.map: f =>
val withCap =
if p.flags.physCap then f.copy(capitalStock = PLN(Firm.workerCount(f).toDouble * p.capital.klRatios.map(_.toDouble)(f.sector.toInt)))
if p.flags.physCap then f.copy(capitalStock = p.capital.klRatios(f.sector.toInt) * Firm.workerCount(f).toDouble)
else f
withCap.copy(bankId = Banking.assignBank(f.sector, Banking.DefaultConfigs, rng))

Expand All @@ -121,26 +121,26 @@ object FirmInit:
*/
private def finalize(firms: Vector[Firm.State])(using p: SimParams): Vector[Firm.State] =
val totalWorkers = firms.map(Firm.workerCount).sum
val totalFirmCash = p.banking.initDeposits.toDouble * FirmDepositShare
val totalFirmCash = p.banking.initDeposits * FirmDepositShare
firms.map: f =>
val wshare = Firm.workerCount(f).toDouble / totalWorkers
val withInv = initInventory(f)
val withEnergy = initGreenCapital(withInv)
withEnergy.copy(cash = PLN(totalFirmCash * wshare), debt = PLN(p.banking.initLoans.toDouble * wshare))
withEnergy.copy(cash = totalFirmCash * wshare, debt = p.banking.initLoans * wshare)

/** Set initial inventory stock from sector target ratio scaled to firm
* capacity.
*/
private def initInventory(f: Firm.State)(using p: SimParams): Firm.State =
if p.flags.inventory then
val capacity = Firm.computeCapacity(f).toDouble
val targetInv = capacity * p.capital.inventoryTargetRatios.map(_.toDouble)(f.sector.toInt)
f.copy(inventory = PLN(targetInv * p.capital.inventoryInitRatio.toDouble))
val capacity = Firm.computeCapacity(f)
val targetInv = capacity * p.capital.inventoryTargetRatios(f.sector.toInt)
f.copy(inventory = targetInv * p.capital.inventoryInitRatio)
else f

/** Set initial green capital stock from sector-specific green K/L ratio. */
private def initGreenCapital(f: Firm.State)(using p: SimParams): Firm.State =
if p.flags.energy then
val targetGK = Firm.workerCount(f).toDouble * p.climate.greenKLRatios.map(_.toDouble)(f.sector.toInt)
f.copy(greenCapital = PLN(targetGK * p.climate.greenInitRatio.toDouble))
val targetGK = p.climate.greenKLRatios(f.sector.toInt) * Firm.workerCount(f).toDouble
f.copy(greenCapital = targetGK * p.climate.greenInitRatio)
else f
Loading