package params import ( "encoding/json" "fmt" "strconv" "gopkg.in/yaml.v3" ) // Params holds all parameters for the mathematical model. // All values are normalized to per-year units. type Params struct { Time TimeParams `json:"time" yaml:"time"` Adoption AdoptionParams `json:"adoption" yaml:"adoption"` Pricing PricingParams `json:"pricing" yaml:"pricing"` Transactions TransactionParams `json:"transactions" yaml:"transactions"` Municipal MunicipalParams `json:"municipal" yaml:"municipal"` ImplServices ImplementationParams `json:"impl_services" yaml:"impl_services"` Impact ImpactParams `json:"impact" yaml:"impact"` Costs CostParams `json:"costs" yaml:"costs"` Market MarketParams `json:"market" yaml:"market"` UnitEconomics UnitEconomicsParams `json:"unit_economics" yaml:"unit_economics"` Profitability ProfitabilityParams `json:"profitability" yaml:"profitability"` Transport TransportParams `json:"transport" yaml:"transport"` } // TimeParams defines the time horizon for the model. type TimeParams struct { Years []int `json:"years" yaml:"years"` // e.g., [1, 2, 3] for Y1, Y2, Y3 } // AdoptionParams defines customer adoption parameters per year. type AdoptionParams struct { TotalOrgs YearlyInt `json:"total_orgs" yaml:"total_orgs"` // Total organizations onboarded PayingShare YearlyFloat `json:"paying_share" yaml:"paying_share"` // Percentage that are paying (0.0-1.0) } // PricingParams defines subscription pricing tiers and tier mix. type PricingParams struct { Basic float64 `json:"basic" yaml:"basic"` // Monthly price for Basic tier (EUR) Business float64 `json:"business" yaml:"business"` // Monthly price for Business tier (EUR) Enterprise float64 `json:"enterprise" yaml:"enterprise"` // Monthly price for Enterprise tier (EUR) BlendedUplift BlendedUplift `json:"blended_uplift" yaml:"blended_uplift"` // Transaction uplift per tier TierMix YearlyTierMix `json:"tier_mix" yaml:"tier_mix"` // Tier distribution per year } // BlendedUplift defines transaction fee uplift for each tier. type BlendedUplift struct { Basic float64 `json:"basic" yaml:"basic"` // e.g., 0.20 = 20% uplift Business float64 `json:"business" yaml:"business"` // e.g., 0.25 = 25% uplift Enterprise float64 `json:"enterprise" yaml:"enterprise"` // e.g., 0.25 = 25% uplift } // TierMix defines the distribution of paying customers across tiers. type TierMix struct { Basic float64 `json:"basic" yaml:"basic"` // e.g., 0.54 = 54% Business float64 `json:"business" yaml:"business"` // e.g., 0.38 = 38% Enterprise float64 `json:"enterprise" yaml:"enterprise"` // e.g., 0.08 = 8% } // TransactionParams defines transaction revenue parameters. type TransactionParams struct { AvgIntroFee float64 `json:"avg_intro_fee" yaml:"avg_intro_fee"` // Average fee per introduction (EUR) IntrosPerYear YearlyInt `json:"intros_per_year" yaml:"intros_per_year"` // Introductions per year IntroConversion YearlyFloat `json:"intro_conversion" yaml:"intro_conversion"` // Conversion rate (0.0-1.0) ServiceGMV YearlyFloat `json:"service_gmv" yaml:"service_gmv"` // Service marketplace GMV (EUR) ServiceCommission float64 `json:"service_commission" yaml:"service_commission"` // Commission rate (0.0-1.0) GroupGMV YearlyFloat `json:"group_gmv" yaml:"group_gmv"` // Group buying GMV (EUR) GroupCommission float64 `json:"group_commission" yaml:"group_commission"` // Commission rate (0.0-1.0) } // MunicipalParams defines municipal/license revenue parameters. type MunicipalParams struct { Cities YearlyInt `json:"cities" yaml:"cities"` // Number of cities with licenses AvgLicense YearlyFloat `json:"avg_license" yaml:"avg_license"` // Average license fee per city (EUR/year) DataLicensing YearlyFloat `json:"data_licensing" yaml:"data_licensing"` // Data licensing revenue (EUR/year) } // ImplementationParams defines implementation services revenue parameters. type ImplementationParams struct { MatchesPerOrg float64 `json:"matches_per_org" yaml:"matches_per_org"` // Average matches per organization PaidShare float64 `json:"paid_share" yaml:"paid_share"` // Share using paid implementation (0.0-1.0) AvgFee float64 `json:"avg_fee" yaml:"avg_fee"` // Average implementation fee (EUR) } // ImpactParams defines environmental impact calculation parameters. type ImpactParams struct { HeatMWh YearlyFloat `json:"heat_mwh" yaml:"heat_mwh"` // Heat recovered (MWh/year) GridFactor float64 `json:"grid_factor" yaml:"grid_factor"` // Grid emission factor (t CO₂/MWh) HXEff float64 `json:"hx_eff" yaml:"hx_eff"` // Heat exchanger efficiency (0.0-1.0) Utilization float64 `json:"utilization" yaml:"utilization"` // Utilization rate (0.0-1.0) WaterPerOrg float64 `json:"water_per_org" yaml:"water_per_org"` // Water flow per organization (m³/year) WaterReuseRate YearlyFloat `json:"water_reuse_rate" yaml:"water_reuse_rate"` // Water reuse rate (0.0-1.0) WastePerOrg float64 `json:"waste_per_org" yaml:"waste_per_org"` // Waste generation per organization (t/year) WasteDiversionRate YearlyFloat `json:"waste_diversion_rate" yaml:"waste_diversion_rate"` // Waste diversion rate (0.0-1.0) } // CostParams defines cost structure parameters. type CostParams struct { Engineers YearlyInt `json:"engineers" yaml:"engineers"` // Number of engineers EngineerSalary float64 `json:"engineer_salary" yaml:"engineer_salary"` // Average engineer salary (EUR/year) Infrastructure YearlyFloat `json:"infrastructure" yaml:"infrastructure"` // Infrastructure costs (EUR/year) MarketingSales YearlyFloat `json:"marketing_sales" yaml:"marketing_sales"` // Marketing & sales costs (EUR/year) Operations YearlyFloat `json:"operations" yaml:"operations"` // Operations costs (EUR/year) } // MarketParams defines market size constants for documentation context. // These represent the problem space, not drivers of calculations. type MarketParams struct { TAM float64 `json:"tam" yaml:"tam"` // Total Addressable Market (EUR) AddressableDigital float64 `json:"addressable_digital" yaml:"addressable_digital"` // Addressable via digital platforms (EUR) PilotCityEconomicBenefit float64 `json:"pilot_city_economic_benefit" yaml:"pilot_city_economic_benefit"` // Economic benefit per city (EUR/year) ScalabilityPotential float64 `json:"scalability_potential" yaml:"scalability_potential"` // Scalability potential if replicated (EUR/year) EUIndustrialFacilities int `json:"eu_industrial_facilities" yaml:"eu_industrial_facilities"` // EU industrial facilities count EnergyWastePotential float64 `json:"energy_waste_potential" yaml:"energy_waste_potential"` // Energy waste recovery potential (0.0-1.0) ResourceCostReduction float64 `json:"resource_cost_reduction" yaml:"resource_cost_reduction"` // Resource cost reduction via symbiosis (0.0-1.0) ViableExchangeRate float64 `json:"viable_exchange_rate" yaml:"viable_exchange_rate"` // Viable exchange rate for SAM calc (0.0-1.0) PlatformCaptureRate float64 `json:"platform_capture_rate" yaml:"platform_capture_rate"` // Platform capture rate for SAM calc (0.0-1.0) SOM YearlyFloat `json:"som" yaml:"som"` // Serviceable Obtainable Market per year (EUR) } // UnitEconomicsParams defines unit economics and customer lifecycle parameters. type UnitEconomicsParams struct { ChurnRates TierRates `json:"churn_rates" yaml:"churn_rates"` // Annual churn rates by tier RetentionMonths TierRates `json:"retention_months" yaml:"retention_months"` // Average retention periods (months) TransactionFees TierRates `json:"transaction_fees" yaml:"transaction_fees"` // Additional transaction revenue per tier per year UpsellRates UpsellRates `json:"upsell_rates" yaml:"upsell_rates"` // Probability of upgrading to next tier EnterpriseExpansion EnterpriseExpansion `json:"enterprise_expansion" yaml:"enterprise_expansion"` // Multi-site expansion for enterprise PlatformCosts float64 `json:"platform_costs" yaml:"platform_costs"` // Platform/transaction costs as % of revenue } // TierRates represents rates for each tier (basic, business, enterprise). type TierRates struct { Basic float64 `json:"basic" yaml:"basic"` Business float64 `json:"business" yaml:"business"` Enterprise float64 `json:"enterprise" yaml:"enterprise"` } // UpsellRates represents upgrade probabilities between tiers. type UpsellRates struct { BasicToBusiness float64 `json:"basic_to_business" yaml:"basic_to_business"` BusinessToEnterprise float64 `json:"business_to_enterprise" yaml:"business_to_enterprise"` } // EnterpriseExpansion represents multi-site expansion for enterprise customers. type EnterpriseExpansion struct { MultiSiteRate float64 `json:"multi_site_rate" yaml:"multi_site_rate"` // Percentage of enterprise customers that expand AdditionalSites float64 `json:"additional_sites" yaml:"additional_sites"` // Average additional sites per customer SiteRevenue float64 `json:"site_revenue" yaml:"site_revenue"` // Monthly revenue per additional site } // ProfitabilityParams defines parameters for profitability analysis. type ProfitabilityParams struct { DiscountRate float64 `json:"discount_rate" yaml:"discount_rate"` // Discount rate for NPV calculations (0.0-1.0) } // TransportParams defines parameters for transport cost calculations. type TransportParams struct { // Heat transport constants HeatFixedCostPerMeter float64 `json:"heat_fixed_cost_per_meter" yaml:"heat_fixed_cost_per_meter"` HeatVariableCostPerMeter float64 `json:"heat_variable_cost_per_meter" yaml:"heat_variable_cost_per_meter"` HeatEnergyCostPerKwh float64 `json:"heat_energy_cost_per_kwh" yaml:"heat_energy_cost_per_kwh"` HeatLossFactor float64 `json:"heat_loss_factor" yaml:"heat_loss_factor"` // Water transport constants WaterBaseCapitalCost float64 `json:"water_base_capital_cost" yaml:"water_base_capital_cost"` WaterCapitalCostPerKm float64 `json:"water_capital_cost_per_km" yaml:"water_capital_cost_per_km"` WaterPumpingCostFactor float64 `json:"water_pumping_cost_factor" yaml:"water_pumping_cost_factor"` WaterDensity float64 `json:"water_density" yaml:"water_density"` WaterGravity float64 `json:"water_gravity" yaml:"water_gravity"` WaterPumpHead float64 `json:"water_pump_head" yaml:"water_pump_head"` WaterPumpEfficiency float64 `json:"water_pump_efficiency" yaml:"water_pump_efficiency"` // Solids transport constants SolidsTransportCostPerTonneKm float64 `json:"solids_transport_cost_per_tonne_km" yaml:"solids_transport_cost_per_tonne_km"` SolidsBaseCapitalCost float64 `json:"solids_base_capital_cost" yaml:"solids_base_capital_cost"` // Gas transport constants GasDensity float64 `json:"gas_density" yaml:"gas_density"` GasCompressionCostPerTonneKm float64 `json:"gas_compression_cost_per_tonne_km" yaml:"gas_compression_cost_per_tonne_km"` GasPipelineCostPerTonneKm float64 `json:"gas_pipeline_cost_per_tonne_km" yaml:"gas_pipeline_cost_per_tonne_km"` GasBaseCapitalCost float64 `json:"gas_base_capital_cost" yaml:"gas_base_capital_cost"` GasCapitalCostPerKm float64 `json:"gas_capital_cost_per_km" yaml:"gas_capital_cost_per_km"` // General constants DefaultOperatingHours int `json:"default_operating_hours" yaml:"default_operating_hours"` } // YearlyInt is a map from year index (string) to integer value. type YearlyInt map[string]int // YearlyFloat is a map from year index (string) to float value. type YearlyFloat map[string]float64 // UnmarshalYAML implements custom YAML unmarshaling for YearlyFloat func (yf *YearlyFloat) UnmarshalYAML(unmarshal func(interface{}) error) error { var raw map[interface{}]interface{} if err := unmarshal(&raw); err != nil { return err } *yf = make(YearlyFloat) for k, v := range raw { key := fmt.Sprintf("%v", k) switch val := v.(type) { case int: (*yf)[key] = float64(val) case float64: (*yf)[key] = val default: return fmt.Errorf("invalid value type for year %s: %T", key, v) } } return nil } // YearlyTierMix is a map from year index (string) to tier mix. type YearlyTierMix map[string]TierMix // LoadFromJSON loads parameters from a JSON byte slice. func LoadFromJSON(data []byte) (*Params, error) { var p Params if err := json.Unmarshal(data, &p); err != nil { return nil, err } return &p, nil } // LoadFromYAML loads parameters from a YAML byte slice. func LoadFromYAML(data []byte) (*Params, error) { var p Params if err := yaml.Unmarshal(data, &p); err != nil { return nil, err } return &p, nil } // GetYearInt returns an integer value for a given year, or 0 if not found. func (yi YearlyInt) GetYear(year int) int { return yi[yearKey(year)] } // GetYearFloat returns a float value for a given year, or 0.0 if not found. func (yf YearlyFloat) GetYear(year int) float64 { return yf[yearKey(year)] } // GetYearTierMix returns a tier mix for a given year. func (ytm YearlyTierMix) GetYear(year int) TierMix { return ytm[yearKey(year)] } // yearKey converts a year index to a string key. func yearKey(year int) string { return strconv.Itoa(year) } // YearKey is a public version of yearKey for use in tests. func YearKey(year int) string { return yearKey(year) } // Validate performs basic validation on parameters. func (p *Params) Validate() error { // Basic sanity checks if len(p.Time.Years) == 0 { return ErrInvalidParams{Field: "time.years", Message: "must have at least one year"} } // Check that all years have corresponding values for required fields for _, year := range p.Time.Years { // Adoption parameters if p.Adoption.TotalOrgs.GetYear(year) == 0 { return ErrInvalidParams{Field: "adoption.total_orgs", Message: "missing value for year " + yearKey(year)} } if p.Adoption.PayingShare.GetYear(year) == 0 { return ErrInvalidParams{Field: "adoption.paying_share", Message: "missing value for year " + yearKey(year)} } // Pricing parameters if p.Pricing.TierMix.GetYear(year) == (TierMix{}) { return ErrInvalidParams{Field: "pricing.tier_mix", Message: "missing value for year " + yearKey(year)} } // Transaction parameters if p.Transactions.IntrosPerYear.GetYear(year) == 0 { return ErrInvalidParams{Field: "transactions.intros_per_year", Message: "missing value for year " + yearKey(year)} } if p.Transactions.IntroConversion.GetYear(year) == 0 { return ErrInvalidParams{Field: "transactions.intro_conversion", Message: "missing value for year " + yearKey(year)} } if p.Transactions.ServiceGMV.GetYear(year) == 0 { return ErrInvalidParams{Field: "transactions.service_gmv", Message: "missing value for year " + yearKey(year)} } if p.Transactions.GroupGMV.GetYear(year) == 0 { return ErrInvalidParams{Field: "transactions.group_gmv", Message: "missing value for year " + yearKey(year)} } // Municipal parameters if p.Municipal.Cities.GetYear(year) == 0 { return ErrInvalidParams{Field: "municipal.cities", Message: "missing value for year " + yearKey(year)} } if p.Municipal.AvgLicense.GetYear(year) == 0 { return ErrInvalidParams{Field: "municipal.avg_license", Message: "missing value for year " + yearKey(year)} } // Impact parameters if p.Impact.HeatMWh.GetYear(year) == 0 { return ErrInvalidParams{Field: "impact.heat_mwh", Message: "missing value for year " + yearKey(year)} } if p.Impact.WaterReuseRate.GetYear(year) == 0 { return ErrInvalidParams{Field: "impact.water_reuse_rate", Message: "missing value for year " + yearKey(year)} } if p.Impact.WasteDiversionRate.GetYear(year) == 0 { return ErrInvalidParams{Field: "impact.waste_diversion_rate", Message: "missing value for year " + yearKey(year)} } // Cost parameters if p.Costs.Engineers.GetYear(year) == 0 { return ErrInvalidParams{Field: "costs.engineers", Message: "missing value for year " + yearKey(year)} } if p.Costs.MarketingSales.GetYear(year) == 0 { return ErrInvalidParams{Field: "costs.marketing_sales", Message: "missing value for year " + yearKey(year)} } if p.Costs.Operations.GetYear(year) == 0 { return ErrInvalidParams{Field: "costs.operations", Message: "missing value for year " + yearKey(year)} } // Market parameters if p.Market.SOM.GetYear(year) == 0 { return ErrInvalidParams{Field: "market.som", Message: "missing value for year " + yearKey(year)} } } return nil } // ErrInvalidParams represents a parameter validation error. type ErrInvalidParams struct { Field string Message string } func (e ErrInvalidParams) Error() string { return "invalid params [" + e.Field + "]: " + e.Message }