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, } }