turash/models/scenarios/scenarios.go
Damir Mukimov 4a2fda96cd
Initial commit: Repository setup with .gitignore, golangci-lint v2.6.0, and code quality checks
- Initialize git repository
- Add comprehensive .gitignore for Go projects
- Install golangci-lint v2.6.0 (latest v2) globally
- Configure .golangci.yml with appropriate linters and formatters
- Fix all formatting issues (gofmt)
- Fix all errcheck issues (unchecked errors)
- Adjust complexity threshold for validation functions
- All checks passing: build, test, vet, lint
2025-11-01 07:36:22 +01:00

170 lines
5.3 KiB
Go

package scenarios
import (
"github.com/damirmukimov/city_resource_graph/models"
"github.com/damirmukimov/city_resource_graph/models/params"
)
// ScenarioResult contains the results of a scenario analysis.
type ScenarioResult struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters ScenarioParams `json:"parameters"`
Result *models.ModelResult `json:"result"`
KeyMetrics KeyMetrics `json:"key_metrics"`
}
// ScenarioParams describes the parameter changes for a scenario.
type ScenarioParams struct {
AdoptionMultiplier float64 `json:"adoption_multiplier"` // Multiplier for adoption rates
PricingMultiplier float64 `json:"pricing_multiplier"` // Multiplier for pricing
CostMultiplier float64 `json:"cost_multiplier"` // Multiplier for costs
}
// KeyMetrics contains the most important metrics for comparison.
type KeyMetrics struct {
TotalRevenue float64 `json:"total_revenue"`
TotalCosts float64 `json:"total_costs"`
TotalProfit float64 `json:"total_profit"`
NPV float64 `json:"npv"`
IRR float64 `json:"irr"`
PaybackYears float64 `json:"payback_years"`
Year3Customers int `json:"year3_customers"`
Year3Revenue float64 `json:"year3_revenue"`
}
// RunSensitivityAnalysis runs multiple scenarios with parameter variations.
func RunSensitivityAnalysis(baseParams *params.Params) []ScenarioResult {
scenarios := []ScenarioResult{
{
Name: "Base Case",
Description: "Original parameters as configured",
Parameters: ScenarioParams{
AdoptionMultiplier: 1.0,
PricingMultiplier: 1.0,
CostMultiplier: 1.0,
},
},
{
Name: "Optimistic",
Description: "20% higher adoption, 10% higher pricing, 10% lower costs",
Parameters: ScenarioParams{
AdoptionMultiplier: 1.2,
PricingMultiplier: 1.1,
CostMultiplier: 0.9,
},
},
{
Name: "Conservative",
Description: "20% lower adoption, 10% lower pricing, 10% higher costs",
Parameters: ScenarioParams{
AdoptionMultiplier: 0.8,
PricingMultiplier: 0.9,
CostMultiplier: 1.1,
},
},
{
Name: "High Growth",
Description: "50% higher adoption, same pricing and costs",
Parameters: ScenarioParams{
AdoptionMultiplier: 1.5,
PricingMultiplier: 1.0,
CostMultiplier: 1.0,
},
},
{
Name: "Cost Focused",
Description: "Same adoption and pricing, 20% lower costs",
Parameters: ScenarioParams{
AdoptionMultiplier: 1.0,
PricingMultiplier: 1.0,
CostMultiplier: 0.8,
},
},
}
results := make([]ScenarioResult, len(scenarios))
for i, scenario := range scenarios {
// Create modified parameters for this scenario
scenarioParams := createScenarioParams(baseParams, scenario.Parameters)
// Run the model with modified parameters
result, err := models.Calculate(scenarioParams)
if err != nil {
// In a real implementation, you'd want to handle errors better
continue
}
// Extract key metrics
keyMetrics := extractKeyMetrics(result)
results[i] = ScenarioResult{
Name: scenario.Name,
Description: scenario.Description,
Parameters: scenario.Parameters,
Result: result,
KeyMetrics: keyMetrics,
}
}
return results
}
// createScenarioParams creates modified parameters for a scenario.
func createScenarioParams(base *params.Params, mods ScenarioParams) *params.Params {
// Create a deep copy of the base parameters
// For simplicity, we'll modify the key fields directly
// In a production system, you'd want proper deep copying
scenario := *base // Shallow copy
// Modify adoption rates
if mods.AdoptionMultiplier != 1.0 {
for yearStr, original := range base.Adoption.TotalOrgs {
scenario.Adoption.TotalOrgs[yearStr] = int(float64(original) * mods.AdoptionMultiplier)
}
}
// Modify pricing
if mods.PricingMultiplier != 1.0 {
scenario.Pricing.Basic = base.Pricing.Basic * mods.PricingMultiplier
scenario.Pricing.Business = base.Pricing.Business * mods.PricingMultiplier
scenario.Pricing.Enterprise = base.Pricing.Enterprise * mods.PricingMultiplier
}
// Modify costs
if mods.CostMultiplier != 1.0 {
for yearStr, engineers := range base.Costs.Engineers {
scenario.Costs.Engineers[yearStr] = int(float64(engineers) * mods.CostMultiplier)
}
for yearStr, infra := range base.Costs.Infrastructure {
scenario.Costs.Infrastructure[yearStr] = infra * mods.CostMultiplier
}
for yearStr, marketing := range base.Costs.MarketingSales {
scenario.Costs.MarketingSales[yearStr] = marketing * mods.CostMultiplier
}
for yearStr, ops := range base.Costs.Operations {
scenario.Costs.Operations[yearStr] = ops * mods.CostMultiplier
}
}
return &scenario
}
// extractKeyMetrics pulls the most important metrics from a model result.
func extractKeyMetrics(result *models.ModelResult) KeyMetrics {
year3 := result.Years[len(result.Years)-1] // Last year (Year 3)
return KeyMetrics{
TotalRevenue: result.Summary.TotalRevenue,
TotalCosts: result.Summary.TotalCosts,
TotalProfit: result.Summary.TotalProfit,
NPV: result.Profitability.NPV,
IRR: result.Profitability.IRR,
PaybackYears: result.Profitability.PaybackPeriod,
Year3Customers: year3.Customer.TotalOrgs,
Year3Revenue: year3.Revenue.Total,
}
}