@@ -43,12 +43,6 @@ function _validate_nonneg_finite(val::Real, field::String)
4343 end
4444end
4545
46- function _validate_pos_finite (val:: Real , field:: String )
47- if ! isfinite (val) || val <= 0.0
48- throw (ArgumentError (" $field must be finite and > 0.0, got $val " ))
49- end
50- end
51-
5246function _validate_basis_energy_unit (basis:: EmissionBasis , energy_unit:: EnergyUnit )
5347 if basis == EmissionBasis. FUEL_INPUT
5448 if energy_unit != EnergyUnit. MMBTU && energy_unit != EnergyUnit. GJ
@@ -66,9 +60,63 @@ function _validate_basis_energy_unit(basis::EmissionBasis, energy_unit::EnergyUn
6660 ),
6761 )
6862 end
63+ else
64+ throw (
65+ ArgumentError (
66+ " unhandled EmissionBasis $basis ; update _validate_basis_energy_unit" ,
67+ ),
68+ )
6969 end
7070end
7171
72+ # Emission-rate helpers (shared between constructor and setters)
73+
74+ """ Wrap a scalar emission rate in a constant-rate `IncrementalCurve` after validation."""
75+ function _emission_rate_curve (val:: Real )
76+ _validate_nonneg_finite (val, " emission_rate" )
77+ return IS. IncrementalCurve (LinearFunctionData (0.0 , Float64 (val)), nothing , nothing )
78+ end
79+
80+ """
81+ Validate that an `emission_rate` [`ValueCurve`](@ref) holds only finite coefficients and
82+ non-negative rate values (the rate at zero input and every tabulated rate must be `>= 0`).
83+ """
84+ function _validate_emission_rate_curve (curve:: IS.ValueCurve )
85+ _validate_emission_rate_function_data (get_function_data (curve))
86+ return
87+ end
88+
89+ function _validate_emission_rate_function_data (fd:: LinearFunctionData )
90+ _validate_nonneg_finite (get_constant_term (fd), " emission_rate (rate at zero input)" )
91+ isfinite (get_proportional_term (fd)) || throw (
92+ ArgumentError (
93+ " emission_rate slope must be finite, got $(get_proportional_term (fd)) " ,
94+ ),
95+ )
96+ return
97+ end
98+
99+ function _validate_emission_rate_function_data (fd:: QuadraticFunctionData )
100+ _validate_nonneg_finite (get_constant_term (fd), " emission_rate (rate at zero input)" )
101+ for term in (get_proportional_term (fd), get_quadratic_term (fd))
102+ isfinite (term) ||
103+ throw (ArgumentError (" emission_rate coefficients must be finite, got $term " ))
104+ end
105+ return
106+ end
107+
108+ function _validate_emission_rate_function_data (
109+ fd:: Union{PiecewiseLinearData, PiecewiseStepData} ,
110+ )
111+ for r in get_y_coords (fd)
112+ _validate_nonneg_finite (r, " emission_rate" )
113+ end
114+ return
115+ end
116+
117+ # Safe fallback for any future FunctionData subtype not enumerated above.
118+ _validate_emission_rate_function_data (:: IS.FunctionData ) = nothing
119+
72120"""
73121 EmissionsData(; name, pollutant, emission_rate, basis, energy_unit, ...)
74122
@@ -92,20 +140,16 @@ function EmissionsData(;
92140 internal:: InfrastructureSystemsInternal = InfrastructureSystemsInternal (),
93141)
94142 _validate_nonneg_finite (start_up_adder, " start_up_adder" )
95- _validate_pos_finite (gwp, " gwp" )
143+ _validate_nonneg_finite (gwp, " gwp" )
96144
97145 # Validate basis/energy_unit combination
98146 _validate_basis_energy_unit (basis, energy_unit)
99147
100- # Convert scalar to IncrementalCurve with constant rate
148+ # Normalize emission_rate to a validated ValueCurve
101149 if emission_rate isa Real
102- _validate_nonneg_finite (emission_rate, " emission_rate" )
103- rate = IS. IncrementalCurve (
104- LinearFunctionData (0.0 , Float64 (emission_rate)),
105- nothing ,
106- nothing ,
107- )
150+ rate = _emission_rate_curve (emission_rate)
108151 else
152+ _validate_emission_rate_curve (emission_rate)
109153 rate = emission_rate
110154 end
111155
@@ -152,15 +196,14 @@ get_internal(value::EmissionsData) = value.internal
152196
153197""" Set [`EmissionsData`](@ref) `emission_rate` with a [`ValueCurve`](@ref)."""
154198function set_emission_rate! (value:: EmissionsData , val:: IS.ValueCurve )
199+ _validate_emission_rate_curve (val)
155200 value. emission_rate = val
156201 return
157202end
158203
159204""" Set [`EmissionsData`](@ref) `emission_rate` with a scalar (wraps in `IncrementalCurve` with constant rate)."""
160205function set_emission_rate! (value:: EmissionsData , val:: Real )
161- _validate_nonneg_finite (val, " emission_rate" )
162- value. emission_rate =
163- IS. IncrementalCurve (LinearFunctionData (0.0 , Float64 (val)), nothing , nothing )
206+ value. emission_rate = _emission_rate_curve (val)
164207 return
165208end
166209
179222
180223""" Set [`EmissionsData`](@ref) `gwp`."""
181224function set_gwp! (value:: EmissionsData , val:: Real )
182- _validate_pos_finite (val, " gwp" )
225+ _validate_nonneg_finite (val, " gwp" )
183226 value. gwp = Float64 (val)
184227 return
185228end
229+
230+ """ Set [`EmissionsData`](@ref) `pollutant`."""
231+ function set_pollutant! (value:: EmissionsData , val:: PollutantType )
232+ value. pollutant = val
233+ return
234+ end
235+
236+ """ Set [`EmissionsData`](@ref) `mass_unit`."""
237+ function set_mass_unit! (value:: EmissionsData , val:: MassUnit )
238+ value. mass_unit = val
239+ return
240+ end
241+
242+ """
243+ Set [`EmissionsData`](@ref) `basis`, validating it against the current `energy_unit`. To
244+ switch between FUEL_INPUT and POWER_OUTPUT (which also requires changing `energy_unit`),
245+ use [`set_basis_and_energy_unit!`](@ref) instead.
246+ """
247+ function set_basis! (value:: EmissionsData , val:: EmissionBasis )
248+ _validate_basis_energy_unit (val, value. energy_unit)
249+ value. basis = val
250+ return
251+ end
252+
253+ """ Set [`EmissionsData`](@ref) `energy_unit`, validating it against the current `basis`."""
254+ function set_energy_unit! (value:: EmissionsData , val:: EnergyUnit )
255+ _validate_basis_energy_unit (value. basis, val)
256+ value. energy_unit = val
257+ return
258+ end
259+
260+ """
261+ Set [`EmissionsData`](@ref) `basis` and `energy_unit` together, validating the combination.
262+ This is the supported way to retarget an attribute between FUEL_INPUT and POWER_OUTPUT,
263+ since neither field can be changed individually without transiently violating the
264+ basis/energy_unit invariant.
265+ """
266+ function set_basis_and_energy_unit! (
267+ value:: EmissionsData ,
268+ basis:: EmissionBasis ,
269+ energy_unit:: EnergyUnit ,
270+ )
271+ _validate_basis_energy_unit (basis, energy_unit)
272+ value. basis = basis
273+ value. energy_unit = energy_unit
274+ return
275+ end
0 commit comments