├── .gitignore ├── LICENSE.md ├── README.md ├── docs ├── RankineLab_technical_note.pdf ├── latex_source.zip └── rankine_cycle_diagrams.svg ├── examples ├── MM │ └── MM.m ├── R600a │ └── R600a.m └── sCO2 │ └── sCO2.m └── source_code ├── create_data_structure.m ├── create_problem_structures.m ├── evaluate_compressor.m ├── evaluate_exchanger.m ├── evaluate_expander.m ├── evaluate_optimization_problem.m ├── evaluate_rankine_cycle.m ├── export_fig ├── .gitignore ├── Export_fig.prj ├── ImageSelection.class ├── ImageSelection.java ├── LICENSE ├── README.md ├── append_pdfs.m ├── copyfig.m ├── crop_borders.m ├── eps2pdf.m ├── export_fig.m ├── fix_lines.m ├── ghostscript.m ├── hyperlink.m ├── im2gif.m ├── isolate_axes.m ├── pdf2eps.m ├── pdftops.m ├── print2array.m ├── print2eps.m ├── read_write_entire_textfile.m ├── resources │ └── project │ │ ├── Extensions.type.Root │ │ └── DependencyAnalysis.type.Extension │ │ │ └── ExternalFiles.type.Extension.xml │ │ ├── Project.xml │ │ ├── ProjectData.type.Info.xml │ │ ├── Root.type.Categories │ │ ├── FileClassCategory.type.Category.xml │ │ └── FileClassCategory.type.Category │ │ │ ├── artifact.type.Label.xml │ │ │ ├── convenience.type.Label.xml │ │ │ ├── derived.type.Label.xml │ │ │ ├── design.type.Label.xml │ │ │ ├── none.type.Label.xml │ │ │ ├── other.type.Label.xml │ │ │ └── test.type.Label.xml │ │ ├── Root.type.Files │ │ ├── .gitignore.type.File.xml │ │ ├── ImageSelection.class.type.File.xml │ │ ├── ImageSelection.java.type.File.xml │ │ ├── LICENSE.type.File.xml │ │ ├── README.md.type.File.xml │ │ ├── append_pdfs.m.type.File.xml │ │ ├── copyfig.m.type.File.xml │ │ ├── crop_borders.m.type.File.xml │ │ ├── eps2pdf.m.type.File.xml │ │ ├── export_fig.m.type.File.xml │ │ ├── fix_lines.m.type.File.xml │ │ ├── ghostscript.m.type.File.xml │ │ ├── im2gif.m.type.File.xml │ │ ├── isolate_axes.m.type.File.xml │ │ ├── pdf2eps.m.type.File.xml │ │ ├── pdftops.m.type.File.xml │ │ ├── print2array.m.type.File.xml │ │ ├── print2eps.m.type.File.xml │ │ ├── read_write_entire_textfile.m.type.File.xml │ │ ├── user_string.m.type.File.xml │ │ └── using_hg2.m.type.File.xml │ │ ├── Root.type.ProjectPath │ │ └── a533e478-c90b-4a1f-a45b-f8877af57bee.type.Reference.xml │ │ └── uuid-cc21a9b0-026d-4b70-9821-3eb23d2abf81.xml ├── user_string.m └── using_hg2.m ├── plot_functions ├── create_plots.m ├── plot_TQ_diagram.m ├── plot_Th_diagram.m ├── plot_Ts_diagram.m └── set_plot_options.m ├── print_solution.m ├── property_functions ├── cluster_func.m ├── liq_line.m ├── prop_calculation.m ├── quality.m ├── sat_line.m └── vap_line.m ├── save_current_solution.m ├── save_solution.m └── solve_optimization_problem.m /.gitignore: -------------------------------------------------------------------------------- 1 | *_results/ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Roberto Agromayor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RankineLab 2 | 3 | ## Description 4 | 5 | `RankineLab` is a MATLAB tool for the thermodynamic analysis and optimization of Rankine cycles. The code was developed aiming at simplicity and flexibility in terms of cycle configurations and operating conditions. Some of the notable features of the code are listed below: 6 | 7 | - Supports simple and recuperated Rankine cycle architectures. 8 | - Supports a wide range of cycle configurations, including: trilateral, partial-evaporation, saturated, superheated, transcritical, and supecritical cycles. 9 | - Supports a wide range of fluids, such as water, carbon dioxide, refrigerants, hydrocarbons and siloxanes. 10 | - Supports first law (energy) and second law (exergy) cycle analyses. 11 | - Uses the equations of state of the [CoolProp](http://www.coolprop.org/) or [REFPROP](https://pages.nist.gov/REFPROP-docs/) fluid libraries to compute the thermodynamic properties of the fluids. 12 | - Uses the gradient-based algorithms of the [MATLAB Optimization Toolbox](https://se.mathworks.com/products/optimization.html) to find the optimal cycle configuration in a systematic way. The usual execution time for one cycle optimization is less than 10 seconds on a single core. 13 | 14 |
15 | 16 |
17 | 18 | ## Installation 19 | 20 | In order to use `RankineLab`, you need a MATLAB installation and the MATLAB Optimization Toolbox. In addition you need to install CoolProp and interface it with MATLAB through Python. This may sound complicated, but it is not! 21 | 22 | Check the step-by-step instructions below to learn how to interface MATLAB with CoolProp in a Windows operating system. The installation for Linux or Mac operating systems would be similar. 23 | 24 | #### Step 1 - Download and install Miniconda 25 | 26 | Download the [Miniconda](https://docs.conda.io/en/latest/miniconda.html) environment management system and follow the installation instructions. 27 | 28 | #### Step 2 - Create a virtual environment and install CoolProp 29 | 30 | Open a Miniconda terminal (look for "Anaconda Prompt" or "Miniconda3"). Once the terminal is open, type the following command to create a new virtual environment named `coolprop_env`: 31 | 32 | ```shell 33 | conda create --name coolprop_env python=3.8 34 | ``` 35 | 36 | Don't worry if you are not familiar with the command window or with virtual environments, you only have to type two more commands before the installation is complete. Now that the environment is created, you can activate it. Just use the following command: 37 | 38 | ```shell 39 | conda activate coolprop_env 40 | ``` 41 | 42 | Finally, type the following command to install CoolProp: 43 | 44 | ```shell 45 | conda install CoolProp --channel conda-forge 46 | ``` 47 | 48 | That's it! Note that it was necessary to tell Miniconda that it should look for Coolprop in the `conda-forge` channel. 49 | 50 | #### Step 3 - Interface MATLAB and Coolprop 51 | 52 | Open MATLAB (or close and open it if it was already open) and type the following command to let MATLAB know where is the Python executable (python.exe) of the virtual environment that you have just created: 53 | 54 | ```matlab 55 | pyversion C:\Users\rober\.conda\envs\coolprop_env\python.exe 56 | ``` 57 | 58 | Note that, in my case, the executable was located at `C:\Users\rober\.conda\envs\coolprop_env\`. You should replace this part with the correct path in your computer. 59 | 60 | Good! You have installed CoolProp and interfaced it with MATLAB. Let's do a simple test to check if the installation was successful. We are going to use CoolProp to compute the saturation temperature of water at atmospheric pressure. Just type the following command in MATLAB: 61 | 62 | ```matlab 63 | py.CoolProp.CoolProp.PropsSI('T','P',101325,'Q',0,'Water') 64 | ``` 65 | 66 | 67 | If it does not throw and error and returns 373.1243 K, the installation was successful. 68 | 69 | ## Getting started 70 | 71 | The best way to learn how to use `RankineLab` is to open one of the [examples](examples/) and start playing around with the different parameters and settings. The examples have plenty of comments to guide the users and help them understand how the code works. You can use the example scripts as a template to start your own projects. 72 | 73 | ## Mathematical background 74 | 75 | Check out the [technical note](./docs/RankineLab_technical_note.pdf) if you want to learn more about the formulation of the optimization problem and the thermodynamic modeling behind `RankOpt`. 76 | 77 | ## License 78 | 79 | `RankineLab` is licensed under the terms of the MIT license. See the [license file](LICENSE.md) for more information. 80 | 81 | ## Contact information 82 | 83 | `RankineLab` was developed by [Roberto Agromayor](https://www.ntnu.edu/employees/roberto.agromayor) and of [Lars O. Nord](https://www.ntnu.edu/employees/lars.nord) at the [Norwegian University of Science and Technology (NTNU)](https://www.ntnu.no/). Drop us an email to [roberto.agromayor@ntnu.no](mailto:roberto.agromayor@ntnu.no) if you have questions about the code or you have a bug to report! 84 | -------------------------------------------------------------------------------- /docs/RankineLab_technical_note.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turbo-sim/RankineLab/f09b7ee6ddc17f0987951fc74d79dc2c02faf829/docs/RankineLab_technical_note.pdf -------------------------------------------------------------------------------- /docs/latex_source.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turbo-sim/RankineLab/f09b7ee6ddc17f0987951fc74d79dc2c02faf829/docs/latex_source.zip -------------------------------------------------------------------------------- /examples/MM/MM.m: -------------------------------------------------------------------------------- 1 | %% RankineLab - A MATLAB program to optimize Rankine cycles 2 | % Authors: Roberto Agromayor 3 | % Date: Spring 2021 4 | 5 | 6 | %% Initialize the program 7 | % Clear all variables and close all figures 8 | clear all 9 | close all 10 | clc 11 | 12 | % Add the path to the 'source_code' directory 13 | % The function genpath() is very convenient 14 | addpath(genpath('../../source_code')) 15 | 16 | % Set a project name (mfilename corresponds to the script name) 17 | project_name = mfilename; 18 | 19 | % Create a directory to store the figures and results 20 | results_path = fullfile(pwd, [project_name, '_results']); 21 | if exist(results_path, 'dir') ~= 7 % 7 is a MATLAB's convention 22 | mkdir(results_path) 23 | else 24 | rmdir(results_path, 's') 25 | mkdir(results_path) 26 | end 27 | 28 | 29 | %% Define the thermodynamic specifications and cycle parameters 30 | % Ambient state 31 | p_0 = 101325; % Ambient pressure 32 | T_0 = 20+273.15; % Ambient temperature 33 | 34 | % Define the working fluids 35 | heating_fluid = 'HEOS::Air'; 36 | working_fluid = 'HEOS::MM'; 37 | cooling_fluid = 'HEOS::Water'; 38 | 39 | % Some guidance to select the fluid name and EoS 40 | %{ 41 | 42 | You can set the back-end used to compute the thermodynamic properties by 43 | precedding the fluid name by 'HEOS::' (for the Coolprop back-end) or by 44 | 'REFPROP::' (for the REFPROP backend). 45 | 46 | You can check the list of the CoolProp back-end fluid names here: 47 | http://www.coolprop.org/fluid_properties/PurePseudoPure.html 48 | 49 | Note that the fluid names for REFPROP may be different 50 | 51 | The equations of state may fail for some thermodynamic states and break 52 | down the optimization progress (oh, the horror...) 53 | 54 | This is depedent on the fluid and the EoS back-end (REFPROP/CoolProp) 55 | 56 | My recommendation is to try the CoolProp back-end first 57 | 1) CoolProp EoS are (much) faster, but can fail sometimes 58 | 2) REFPROP EoS are slower, but they are also a bit more robust 59 | 60 | The worst kind of error that you can get while executing the code is 61 | that the equations of state fail. 62 | 63 | There is not much to do about it when it happens because it depends on the 64 | CoolProp/REFPROP libraries. 65 | 66 | If you encounter this problem you can try to start the optimization from a 67 | different initial guess, change the bounds/constraints of the optimization 68 | or, as a last measure, change the EoS back-to REFPROP. 69 | 70 | If all of these fail, you are out of luck... 71 | 72 | %} 73 | 74 | % Define the thermodynamic specifications of the heat source 75 | m_h = 1.00; % Heat source mass flow rate (kg/s). This variable "scales-up" the problem 76 | T_1 = 300+273.15; % Heat source inlet temperature (K) 77 | p_1 = p_0; % Heat source inlet pressure (Pa) 78 | p_3 = p_0; % Heat source outlet pressure (Pa) 79 | T_3min = 120+273.15; % Minimum temperature at the outlet of the heat source (K) 80 | T_3max = T_1; % Maximum temperature at the outlet of the heat source (K) 81 | 82 | % Define the thermodynamic specifications of the heat sink 83 | T_10 = T_0; % Heat sink inlet temperature (K) 84 | p_10 = p_0; % Heat sink inlet pressure (Pa) 85 | p_12 = p_0; % Heat sink outlet pressure (Pa) 86 | T_12min = T_0+10; % Minimum temperature at the outlet of the heat sink (K) 87 | T_12max = T_0+20; % Maximum temperature at the outlet of the heat sink (K) 88 | 89 | % Define pressure drops in the heat exchangers (% of the inlet pressure) 90 | dp_h_evap = 0.02; % Pressure drop in the heating fluid side of the evaporator (relative with respect to inlet) 91 | dp_c_evap = 0.02; % Pressure drop in the working fluid side of the evaporator (relative with respect to inlet) 92 | dp_h_cond = 0.02; % Pressure drop in the working fluid side of the condenser (relative with respect to inlet) 93 | dp_c_cond = 0.02; % Pressure drop in the cooling fluid side of the condenser (relative with respect to inlet) 94 | dp_h_rec = 0.02; % Pressure drop in the hot side of the recuperator (relative with respect to inlet) 95 | dp_c_rec = 0.02; % Pressure drop in the cold side of the recuperator (relative with respect to inlet) 96 | 97 | % Define the number of discretizations of the heat exchangers 98 | % More discretizations: more accurate pinch point and more execution time 99 | N_evap = 50; % Choose an integer 100 | N_cond = 50; % Choose an integer 101 | N_rec = 50; % Choose an integer 102 | 103 | % Define the efficiency of the pumps 104 | eta_pump_h = 0.80; % Isentropic efficiency of the heat source pump 105 | eta_pump_f = 0.80; % Isentropic efficiency of the working fluid pump 106 | eta_pump_c = 0.80; % Isentropic efficiency of the heat sink pump 107 | 108 | % Define the efficiency of the expander 109 | eta_expander = 0.90; % Efficiency of the expander 110 | 111 | % Turbomachinery efficiency definition 112 | pump_efficiency_definition = 'isentropic'; % Definition of the pump efficiency: 'isentropic' or 'polytropic' 113 | expander_efficiency_definition = 'isentropic'; % Definition of the expander efficiency: 'isentropic' or 'polytropic' 114 | 115 | % More details about the efficiency definition 116 | %{ 117 | 118 | If you set efficiency_definition='isentropic' the efficiency of the 119 | working fluid pump and expander are defined as: 120 | 121 | eta_pump = (h_out_s-h_in)/(h_out-h_in) 122 | eta_expander = (h_in-h_out)/(h_in-h_out_s) 123 | 124 | and the turbomachinery computations only involve the inlet and exit states 125 | 126 | If you set efficiency_definition='polytropic' the efficiency of the working 127 | fluid pump and expander are defined as: 128 | 129 | eta_pump = dh_s/dh = 1/d*dp/dh 130 | eta_expander = dh/dh_s = d*dh/dp 131 | 132 | and the turbomachinery computations are performed by solving the previous 133 | ordinary differential equations (ODE) along the compression or expansion 134 | 135 | %} 136 | 137 | 138 | %% Define extra fluid parameters 139 | % Used to scale degrees of freedom (no need to do changes in most cases) 140 | % Define the critical properties 141 | p_crit = prop_calculation('P_CRITICAL',working_fluid); % Critical pressure 142 | T_crit = prop_calculation('T_CRITICAL',working_fluid); % Critical temperature 143 | % p_crit = []; 144 | % T_crit = []; 145 | 146 | % Define the triple properties (only pure substances) 147 | p_trip = prop_calculation('P_TRIPLE',working_fluid); % Triple pressure 148 | T_trip = prop_calculation('T_TRIPLE',working_fluid); % Triple temperature 149 | % p_trip = []; 150 | % T_trip = []; 151 | 152 | % Define the saturation pressure and enthalpy at ambient temperature 153 | % If the ambient temperature is higher than the critical temperature use 154 | % the critical pressure and enthalpy instead 155 | if T_0 < T_crit 156 | p_sat0 = prop_calculation('P','T',T_0,'Q',0,working_fluid); 157 | h_sat0 = prop_calculation('H','T',T_0,'Q',0,working_fluid); 158 | else 159 | p_sat0 = p_crit; 160 | h_sat0 = prop_calculation('H','T',T_crit,'P',p_crit,working_fluid); 161 | end 162 | 163 | % Define the pressure and temperature limits 164 | p_min = 1.01*p_trip; % Minimum pressure 165 | p_max = 5.00*p_crit; % Maximum pressure 166 | T_min = T_0; % Maximum temperature 167 | T_max = 1.50*T_crit; % Maximum temperature 168 | 169 | % Define the enthalpy limits 170 | h_min = h_sat0; 171 | h_max = prop_calculation('H','T',T_1,'P',1e-3,working_fluid); 172 | 173 | 174 | %% Define the independent variables and bounds 175 | % x1 = Heat source exit temperature 176 | x1 = 0.25; 177 | x1_min = 0.00; 178 | x1_max = 1.00; 179 | 180 | % x2 = Heat sink exit temperature 181 | x2 = 0.25; 182 | x2_min = 0.00; 183 | x2_max = 1.00; 184 | 185 | % x3 = Pump inlet pressure (subcritical) 186 | x3 = (1.5*p_sat0-p_min)/(p_max-p_min); 187 | x3_min = 0.00; 188 | x3_max = 1.00; 189 | 190 | % x4 = Pump inlet enthalpy 191 | x4 = 0.05; 192 | x4_min = 0.00; 193 | x4_max = 1.00; 194 | 195 | % x5 = Expander inlet pressure 196 | x5 = (1.5*p_crit-p_min)/(p_max-p_min); 197 | x5_min = 0.00; 198 | x5_max = 1.00; 199 | 200 | % x6 = Expander inlet enthalpy 201 | x6 = 0.80; 202 | x6_min = 0.00; 203 | x6_max = 1.00; 204 | 205 | % x7 = Recuperator effectiveness (set to zero for no recuperator) 206 | x7 = 0.25; 207 | x7_min = 0.00; 208 | x7_max = 1.00; 209 | 210 | % Organize the information in vectors 211 | lb_cycle = [x1_min, x2_min, x3_min, x4_min, x5_min, x6_min, x7_min]'; 212 | ub_cycle = [x1_max, x2_max, x3_max, x4_max, x5_max, x6_max, x7_max]'; 213 | x0_cycle = [x1, x2, x3, x4, x5, x6, x7]'; 214 | 215 | % Some guidelines for the degrees of freedom 216 | %{ 217 | 218 | Play around with x0_cycle to find a good initial guess. 219 | 220 | Mathematical definition of the degrees of freedom 221 | x1 = (T_3 - T_3min) / (T_3max - T_3min) 222 | x2 = (T_12 - T_12min) / (T_12max - T_12min) 223 | x3 = (p_4 - p_min) / (p_max - p_min) 224 | x4 = (h_4 - h_min) / (h_max - h_min) 225 | x5 = (p_7 - p_min) / (p_max - p_min) 226 | x6 = (h_7 - h_min) / (h_max - h_min) 227 | x7 = (h6 - h5) / (h(p6,T8) - h5) 228 | 229 | If you want a cycle configuration with no recuperator you have to: 230 | 1) Set the recuperator effectiveness equal to zero: 231 | x7 = x7_min = x7_max = 0.00 232 | 2) Set the pressure drops in the recuperator equal to zero: 233 | dp_h_rec = dp_c_rec = 0.00 234 | 3) Do not apply a constraint for the recuperator pinch point dT: 235 | constraints.dT_rec.apply = 'no' 236 | 237 | How to give start values when you use the code for the first time? 238 | x1 usually takes low values (maximum exploitation of the heat source) 239 | x2 usually takes low values (small temperature jump in the heat sink) 240 | x3 usually takes low values (low pressure at the exit of the condenser) 241 | x4 usually takes low values (low enthalpy at the exit of the condenser) 242 | x5x_crit for transcritical cycles 243 | x6 usually takes high values (high enthalpy at the inlet of the evap.) 244 | x7=0 corresponds to no recuperation and x=1 to ideal recuperation 245 | 246 | Note: Low means close to zero and high means close to one 247 | 248 | %} 249 | 250 | 251 | %% Define the constraints 252 | % Instructions: 253 | % 1. Specify the minimum and maximum values 254 | % 2. Specify whether to apply the constraint or not 255 | % 3. Use [brackets] to ignore the constraint value 256 | 257 | % Minimum temperature difference in the evaporator (pinch point) 258 | constraints.dT_evap.min = 10; 259 | constraints.dT_evap.apply = 'yes'; 260 | 261 | % Minimum temperature difference in the condenser (pinch point) 262 | constraints.dT_cond.min = 10; 263 | constraints.dT_cond.apply = 'yes'; 264 | 265 | % Minimum temperature difference in the recuperator (pinch point) 266 | constraints.dT_rec.min = 10; 267 | constraints.dT_rec.apply = 'yes'; 268 | 269 | % Minimum vapor quality along the expander 270 | constraints.quality.min = 1.00; 271 | constraints.quality.apply = 'yes'; 272 | 273 | % Degree of subcooling at the inlet of the pump 274 | constraints.dT_subcooling.min = 1; 275 | constraints.dT_subcooling.max = []; 276 | constraints.dT_subcooling.apply = 'yes'; 277 | 278 | % Degree of superheating at inlet of the expander (subcritical cycles) 279 | constraints.dT_superheating.min = 5; 280 | constraints.dT_superheating.max = []; 281 | constraints.dT_superheating.apply = 'no'; 282 | 283 | % Maximum cycle pressure 284 | constraints.pressure.min = []; 285 | constraints.pressure.max = []; 286 | constraints.pressure.apply = 'no'; 287 | 288 | 289 | %% Define optimization algorithm settings 290 | % Set optimization algorithm 291 | % 'spq' is my favourite because it converges reliably and respects bounds 292 | % 'active-set' is a bit more aggressive and does not always respect the 293 | % bounds. It is faster, but less reliable, than 'sqp'. 294 | % 'interior-point' also works, but it requires more iterations 295 | algorithm = 'sqp'; 296 | 297 | % Compute the problem gradients in parallel or not 298 | use_parallel = false; % true or false 299 | 300 | % Define termination criteria (no need to change in most cases) 301 | max_iterations = 1000; 302 | max_function_evals = 10000; 303 | step_tolerance = 1e-08; 304 | function_tolerance = 1e-05; 305 | constraint_tolerance = 1e-05; 306 | optimality_tolerance = 1e-05; 307 | 308 | % Description of the possible outputs of the optimization function 309 | %{ 310 | 311 | 'interior-point', 'active-set', and 'sqp' algorithms 312 | Exitflag = 1 -> Success. First-order optimality measure was less than options and constraints are not violated. 313 | Exitflag = 2 -> Success. Step size was less than options and constraints are not violated. 314 | Exitflag = 0 -> Unsuccess. Number of iterations or function evaluations exceeded option 315 | Exitflag = -1 -> Unsuccess. Solver stopped by an output function 316 | Exitflag = -2 -> Unsuccess. No feasible point was found 317 | 318 | Only active set algorithm 319 | Exitflag = 4 -> Success. Magnitude of the search direction was less than 2*options.StepTolerance and maximum constraint violation was less than options.ConstraintTolerance. 320 | Exitflag = 5 -> Success. Magnitude of directional derivative in search direction was less than 2*options.OptimalityTolerance and maximum constraint violation was less than options.ConstraintTolerance. 321 | 322 | %} 323 | 324 | 325 | %% Choose what figures should be plotted 326 | choose_plots = struct('diagram_TQ', 'yes', ... % Temperature - heat duty diagram 327 | 'diagram_Ts', 'yes', ... % Temperature - entropy diagram of the cycle 328 | 'diagram_Th', 'yes', ... % Temperature - enthalpy diagram of the cycle 329 | 'save', 0); % Choose whether to save the figures or not 330 | 331 | % Description of the saving options 332 | %{ 333 | 334 | save=0 does not save the figures 335 | save=1 saves the figure in vector format using the default MATLAB export function (faster) 336 | save=2 saves the figure in vector using the export_fig library (requires ghostscript) 337 | 338 | In order to use save=2 it is necessary to install 'ghoscript' 339 | https://github.com/altmany/export_fig 340 | http://www.ghostscript.com 341 | http://xpdfreader.com 342 | 343 | %} 344 | 345 | 346 | %% Store the parameters defined up to this point into data structures 347 | % The script 'create_problem_structures' located in source_code folder 348 | % 1) 'fixed_parameters' stores the cycle specifications 349 | % 2) 'optimization_problem' stores the optimization problem settings 350 | create_problem_structures 351 | 352 | % These data structures are created in a different script to keep the 353 | % number of lines of this script to a minimum 354 | 355 | % Be tidy and clear the worskpace now that we stored everything 356 | clearvars -except optimization_problem fixed_parameters project_name results_path 357 | 358 | 359 | %% Load a previous solution as initial guess 360 | % % Uncomment these lines to use a previous solution as initial guess 361 | % load(fullfile(fixed_parameters.results_path,'cycle_data.mat')) 362 | % optimization_problem.x0 = cycle_data.optimization.x; 363 | 364 | 365 | %% Plot the initial solution 366 | filename_suffix = 'initial'; 367 | cycle_data = create_plots(optimization_problem.x0,fixed_parameters,filename_suffix); 368 | print_solution(cycle_data) 369 | 370 | 371 | %% Solve the optimization problem 372 | [cycle_data,x_opt,f_opt,exitflag,output,lambda] = solve_optimization_problem(fixed_parameters,optimization_problem); 373 | save_solution(cycle_data, project_name, results_path) 374 | 375 | %% Plot the optimal solution 376 | filename_suffix = 'optimal'; 377 | create_plots(x_opt,fixed_parameters,filename_suffix); 378 | print_solution(cycle_data) 379 | 380 | 381 | 382 | -------------------------------------------------------------------------------- /examples/R600a/R600a.m: -------------------------------------------------------------------------------- 1 | %% RankineLab - A MATLAB program to optimize Rankine cycles 2 | % Authors: Roberto Agromayor 3 | % Date: Spring 2021 4 | 5 | 6 | %% Initialize the program 7 | % Clear all variables and close all figures 8 | clear all 9 | close all 10 | clc 11 | 12 | % Add the path to the 'source_code' directory 13 | % The function genpath() is very convenient 14 | addpath(genpath('../../source_code')) 15 | 16 | % Set a project name (mfilename corresponds to the script name) 17 | project_name = mfilename; 18 | 19 | % Create a directory to store the figures and results 20 | results_path = fullfile(pwd, [project_name, '_results']); 21 | if exist(results_path, 'dir') ~= 7 % 7 is a MATLAB's convention 22 | mkdir(results_path) 23 | else 24 | rmdir(results_path, 's') 25 | mkdir(results_path) 26 | end 27 | 28 | 29 | %% Define the thermodynamic specifications and cycle parameters 30 | % Ambient state 31 | p_0 = 101325; % Ambient pressure 32 | T_0 = 15+273.15; % Ambient temperature 33 | 34 | % Define the working fluids 35 | heating_fluid = 'HEOS::Water'; 36 | working_fluid = 'HEOS::Isobutane'; 37 | cooling_fluid = 'HEOS::Water'; 38 | 39 | % Some guidance to select the fluid name and EoS 40 | %{ 41 | 42 | You can set the back-end used to compute the thermodynamic properties by 43 | precedding the fluid name by 'HEOS::' (for the Coolprop back-end) or by 44 | 'REFPROP::' (for the REFPROP backend). 45 | 46 | You can check the list of the CoolProp back-end fluid names here: 47 | http://www.coolprop.org/fluid_properties/PurePseudoPure.html 48 | 49 | Note that the fluid names for REFPROP may be different 50 | 51 | The equations of state may fail for some thermodynamic states and break 52 | down the optimization progress (oh, the horror...) 53 | 54 | This is depedent on the fluid and the EoS back-end (REFPROP/CoolProp) 55 | 56 | My recommendation is to try the CoolProp back-end first 57 | 1) CoolProp EoS are (much) faster, but can fail sometimes 58 | 2) REFPROP EoS are slower, but they are also a bit more robust 59 | 60 | The worst kind of error that you can get while executing the code is 61 | that the equations of state fail. 62 | 63 | There is not much to do about it when it happens because it depends on the 64 | CoolProp/REFPROP libraries. 65 | 66 | If you encounter this problem you can try to start the optimization from a 67 | different initial guess, change the bounds/constraints of the optimization 68 | or, as a last measure, change the EoS back-to REFPROP. 69 | 70 | If all of these fail, you are out of luck... 71 | 72 | %} 73 | 74 | % Define the thermodynamic specifications of the heat source 75 | m_h = 1.00; % Heat source mass flow rate (kg/s). This variable "scales-up" the problem 76 | T_1 = 120+273.15; % Heat source inlet temperature (K) 77 | p_1 = 3*p_0; % Heat source inlet pressure (Pa) 78 | p_3 = 3*p_0; % Heat source outlet pressure (Pa) 79 | T_3min = 75+273.15; % Minimum temperature at the outlet of the heat source (K) 80 | T_3max = T_1; % Maximum temperature at the outlet of the heat source (K) 81 | 82 | % Define the thermodynamic specifications of the heat sink 83 | T_10 = 10+273.15; % Heat sink inlet temperature (K) 84 | p_10 = p_0; % Heat sink inlet pressure (Pa) 85 | p_12 = p_0; % Heat sink outlet pressure (Pa) 86 | T_12min = 10+273.15+10; % Minimum temperature at the outlet of the heat sink (K) 87 | T_12max = T_0+20; % Maximum temperature at the outlet of the heat sink (K) 88 | 89 | % Define pressure drops in the heat exchangers (% of the inlet pressure) 90 | dp_h_evap = 0.02; % Pressure drop in the heating fluid side of the evaporator (relative with respect to inlet) 91 | dp_c_evap = 0.02; % Pressure drop in the working fluid side of the evaporator (relative with respect to inlet) 92 | dp_h_cond = 0.02; % Pressure drop in the working fluid side of the condenser (relative with respect to inlet) 93 | dp_c_cond = 0.02; % Pressure drop in the cooling fluid side of the condenser (relative with respect to inlet) 94 | dp_h_rec = 0.00; % Pressure drop in the hot side of the recuperator (relative with respect to inlet) 95 | dp_c_rec = 0.00; % Pressure drop in the cold side of the recuperator (relative with respect to inlet) 96 | 97 | % Define the number of discretizations of the heat exchangers 98 | % More discretizations: more accurate pinch point and more execution time 99 | N_evap = 50; % Choose an integer 100 | N_cond = 50; % Choose an integer 101 | N_rec = 50; % Choose an integer 102 | 103 | % Define the efficiency of the pumps 104 | eta_pump_h = 0.70; % Isentropic efficiency of the heat source pump 105 | eta_pump_f = 0.70; % Isentropic efficiency of the working fluid pump 106 | eta_pump_c = 0.70; % Isentropic efficiency of the heat sink pump 107 | 108 | % Define the efficiency of the expander 109 | eta_expander = 0.70; % Efficiency of the expander 110 | 111 | % Turbomachinery efficiency definition 112 | pump_efficiency_definition = 'isentropic'; % Definition of the pump efficiency: 'isentropic' or 'polytropic' 113 | expander_efficiency_definition = 'isentropic'; % Definition of the expander efficiency: 'isentropic' or 'polytropic' 114 | 115 | % More details about the efficiency definition 116 | %{ 117 | 118 | If you set efficiency_definition='isentropic' the efficiency of the 119 | working fluid pump and expander are defined as: 120 | 121 | eta_pump = (h_out_s-h_in)/(h_out-h_in) 122 | eta_expander = (h_in-h_out)/(h_in-h_out_s) 123 | 124 | and the turbomachinery computations only involve the inlet and exit states 125 | 126 | If you set efficiency_definition='polytropic' the efficiency of the working 127 | fluid pump and expander are defined as: 128 | 129 | eta_pump = dh_s/dh = 1/d*dp/dh 130 | eta_expander = dh/dh_s = d*dh/dp 131 | 132 | and the turbomachinery computations are performed by solving the previous 133 | ordinary differential equations (ODE) along the compression or expansion 134 | 135 | %} 136 | 137 | 138 | %% Define extra fluid parameters 139 | % Used to scale degrees of freedom (no need to do changes in most cases) 140 | % Define the critical properties 141 | p_crit = prop_calculation('P_CRITICAL',working_fluid); % Critical pressure 142 | T_crit = prop_calculation('T_CRITICAL',working_fluid); % Critical temperature 143 | % p_crit = []; 144 | % T_crit = []; 145 | 146 | % Define the triple properties (only pure substances) 147 | p_trip = prop_calculation('P_TRIPLE',working_fluid); % Triple pressure 148 | T_trip = prop_calculation('T_TRIPLE',working_fluid); % Triple temperature 149 | % p_trip = []; 150 | % T_trip = []; 151 | 152 | % Define the saturation pressure and enthalpy at ambient temperature 153 | % If the ambient temperature is higher than the critical temperature use 154 | % the critical pressure and enthalpy instead 155 | if T_0 < T_crit 156 | p_sat0 = prop_calculation('P','T',T_0,'Q',0,working_fluid); 157 | h_sat0 = prop_calculation('H','T',T_0,'Q',0,working_fluid); 158 | else 159 | p_sat0 = p_crit; 160 | h_sat0 = prop_calculation('H','T',T_crit,'P',p_crit,working_fluid); 161 | end 162 | 163 | % Define the pressure and temperature limits 164 | p_min = 1.01*p_trip; % Minimum pressure 165 | p_max = 3.00*p_crit; % Maximum pressure 166 | T_min = T_0; % Maximum temperature 167 | T_max = 1.50*T_crit; % Maximum temperature 168 | 169 | % Define the enthalpy limits 170 | h_min = h_sat0; 171 | h_max = prop_calculation('H','T',T_1,'P',1e-3,working_fluid); 172 | 173 | 174 | %% Define the independent variables and bounds 175 | % x1 = Heat source exit temperature 176 | x1 = 0.25; 177 | x1_min = 0.00; 178 | x1_max = 1.00; 179 | 180 | % x2 = Heat sink exit temperature 181 | x2 = 0.25; 182 | x2_min = 0.00; 183 | x2_max = 1.00; 184 | 185 | % x3 = Pump inlet pressure (subcritical) 186 | x3 = (1.5*p_sat0-p_min)/(p_max-p_min); 187 | x3_min = 0.00; 188 | x3_max = 1.00; 189 | 190 | % x4 = Pump inlet enthalpy 191 | x4 = 0.05; 192 | x4_min = 0.00; 193 | x4_max = 1.00; 194 | 195 | % x5 = Expander inlet pressure 196 | x5 = (0.50*p_crit-p_min)/(p_max-p_min); 197 | x5_min = 0.00; 198 | x5_max = 1.00; 199 | 200 | % x6 = Expander inlet enthalpy 201 | x6 = 0.80; 202 | x6_min = 0.00; 203 | x6_max = 1.00; 204 | 205 | % x7 = Recuperator effectiveness (set to zero for no recuperator) 206 | x7 = 0.00; 207 | x7_min = 0.00; 208 | x7_max = 0.00; 209 | 210 | % Organize the information in vectors 211 | lb_cycle = [x1_min, x2_min, x3_min, x4_min, x5_min, x6_min, x7_min]'; 212 | ub_cycle = [x1_max, x2_max, x3_max, x4_max, x5_max, x6_max, x7_max]'; 213 | x0_cycle = [x1, x2, x3, x4, x5, x6, x7]'; 214 | 215 | % Some guidelines for the degrees of freedom 216 | %{ 217 | 218 | Play around with x0_cycle to find a good initial guess. 219 | 220 | Mathematical definition of the degrees of freedom 221 | x1 = (T_3 - T_3min) / (T_3max - T_3min) 222 | x2 = (T_12 - T_12min) / (T_12max - T_12min) 223 | x3 = (p_4 - p_min) / (p_max - p_min) 224 | x4 = (h_4 - h_min) / (h_max - h_min) 225 | x5 = (p_7 - p_min) / (p_max - p_min) 226 | x6 = (h_7 - h_min) / (h_max - h_min) 227 | x7 = (h6 - h5) / (h(p6,T8) - h5) 228 | 229 | If you want a cycle configuration with no recuperator you have to: 230 | 1) Set the recuperator effectiveness equal to zero: 231 | x7 = x7_min = x7_max = 0.00 232 | 2) Set the pressure drops in the recuperator equal to zero: 233 | dp_h_rec = dp_c_rec = 0.00 234 | 3) Do not apply a constraint for the recuperator pinch point dT: 235 | constraints.dT_rec.apply = 'no' 236 | 237 | How to give start values when you use the code for the first time? 238 | x1 usually takes low values (maximum exploitation of the heat source) 239 | x2 usually takes low values (small temperature jump in the heat sink) 240 | x3 usually takes low values (low pressure at the exit of the condenser) 241 | x4 usually takes low values (low enthalpy at the exit of the condenser) 242 | x5x_crit for transcritical cycles 243 | x6 usually takes high values (high enthalpy at the inlet of the evap.) 244 | x7=0 corresponds to no recuperation and x=1 to ideal recuperation 245 | 246 | Note: Low means close to zero and high means close to one 247 | 248 | %} 249 | 250 | 251 | %% Define the constraints 252 | % Instructions: 253 | % 1. Specify the minimum and maximum values 254 | % 2. Specify whether to apply the constraint or not 255 | % 3. Use [brackets] to ignore the constraint value 256 | 257 | % Minimum temperature difference in the evaporator (pinch point) 258 | constraints.dT_evap.min = 10; 259 | constraints.dT_evap.apply = 'yes'; 260 | 261 | % Minimum temperature difference in the condenser (pinch point) 262 | constraints.dT_cond.min = 10; 263 | constraints.dT_cond.apply = 'yes'; 264 | 265 | % Minimum temperature difference in the recuperator (pinch point) 266 | constraints.dT_rec.min = 10; 267 | constraints.dT_rec.apply = 'no'; 268 | 269 | % Minimum vapor quality along the expander 270 | constraints.quality.min = 1.00; 271 | constraints.quality.apply = 'yes'; 272 | 273 | % Degree of subcooling at the inlet of the pump 274 | constraints.dT_subcooling.min = 1; 275 | constraints.dT_subcooling.max = []; 276 | constraints.dT_subcooling.apply = 'yes'; 277 | 278 | % Degree of superheating at inlet of the expander (subcritical cycles) 279 | constraints.dT_superheating.min = 5; 280 | constraints.dT_superheating.max = []; 281 | constraints.dT_superheating.apply = 'yes'; 282 | 283 | % Maximum cycle pressure 284 | constraints.pressure.min = 1.5e5; 285 | constraints.pressure.max = 15e5; 286 | constraints.pressure.apply = 'yes'; 287 | 288 | 289 | %% Define optimization algorithm settings 290 | % Set optimization algorithm 291 | % 'spq' is my favourite because it converges reliably and respects bounds 292 | % 'active-set' is a bit more aggressive and does not always respect the 293 | % bounds. It is faster, but less reliable, than 'sqp'. 294 | % 'interior-point' also works, but it requires more iterations 295 | algorithm = 'sqp'; 296 | 297 | % Compute the problem gradients in parallel or not 298 | use_parallel = false; % true or false 299 | 300 | % Define termination criteria (no need to change in most cases) 301 | max_iterations = 1000; 302 | max_function_evals = 10000; 303 | step_tolerance = 1e-08; 304 | function_tolerance = 1e-05; 305 | constraint_tolerance = 1e-05; 306 | optimality_tolerance = 1e-05; 307 | 308 | % Description of the possible outputs of the optimization function 309 | %{ 310 | 311 | 'interior-point', 'active-set', and 'sqp' algorithms 312 | Exitflag = 1 -> Success. First-order optimality measure was less than options and constraints are not violated. 313 | Exitflag = 2 -> Success. Step size was less than options and constraints are not violated. 314 | Exitflag = 0 -> Unsuccess. Number of iterations or function evaluations exceeded option 315 | Exitflag = -1 -> Unsuccess. Solver stopped by an output function 316 | Exitflag = -2 -> Unsuccess. No feasible point was found 317 | 318 | Only active set algorithm 319 | Exitflag = 4 -> Success. Magnitude of the search direction was less than 2*options.StepTolerance and maximum constraint violation was less than options.ConstraintTolerance. 320 | Exitflag = 5 -> Success. Magnitude of directional derivative in search direction was less than 2*options.OptimalityTolerance and maximum constraint violation was less than options.ConstraintTolerance. 321 | 322 | %} 323 | 324 | 325 | %% Choose what figures should be plotted 326 | choose_plots = struct('diagram_TQ', 'yes', ... % Temperature - heat duty diagram 327 | 'diagram_Ts', 'yes', ... % Temperature - entropy diagram of the cycle 328 | 'diagram_Th', 'yes', ... % Temperature - enthalpy diagram of the cycle 329 | 'save', 0); % Choose whether to save the figures or not 330 | 331 | % Description of the saving options 332 | %{ 333 | 334 | save=0 does not save the figures 335 | save=1 saves the figure in vector format using the default MATLAB export function (faster) 336 | save=2 saves the figure in vector using the export_fig library (requires ghostscript) 337 | 338 | In order to use save=2 it is necessary to install 'ghoscript' 339 | https://github.com/altmany/export_fig 340 | http://www.ghostscript.com 341 | http://xpdfreader.com 342 | 343 | %} 344 | 345 | 346 | %% Store the parameters defined up to this point into data structures 347 | % The script 'create_problem_structures' located in source_code folder 348 | % 1) 'fixed_parameters' stores the cycle specifications 349 | % 2) 'optimization_problem' stores the optimization problem settings 350 | create_problem_structures 351 | 352 | % These data structures are created in a different script to keep the 353 | % number of lines of this script to a minimum 354 | 355 | % Be tidy and clear the worskpace now that we stored everything 356 | clearvars -except optimization_problem fixed_parameters project_name results_path 357 | 358 | 359 | %% Load a previous solution as initial guess 360 | % % Uncomment these lines to use a previous solution as initial guess 361 | % load(fullfile(fixed_parameters.results_path,'cycle_data.mat')) 362 | % optimization_problem.x0 = cycle_data.optimization.x; 363 | 364 | 365 | %% Plot the initial solution 366 | filename_suffix = 'initial'; 367 | cycle_data = create_plots(optimization_problem.x0,fixed_parameters,filename_suffix); 368 | print_solution(cycle_data) 369 | 370 | 371 | %% Solve the optimization problem 372 | [cycle_data,x_opt,f_opt,exitflag,output,lambda] = solve_optimization_problem(fixed_parameters,optimization_problem); 373 | save_solution(cycle_data, project_name, results_path) 374 | 375 | %% Plot the optimal solution 376 | filename_suffix = 'optimal'; 377 | create_plots(x_opt,fixed_parameters,filename_suffix); 378 | print_solution(cycle_data) 379 | 380 | 381 | 382 | -------------------------------------------------------------------------------- /examples/sCO2/sCO2.m: -------------------------------------------------------------------------------- 1 | %% RankineLab - A MATLAB program to optimize Rankine cycles 2 | % Authors: Roberto Agromayor 3 | % Date: Spring 2021 4 | 5 | 6 | %% Initialize the program 7 | % Clear all variables and close all figures 8 | clear all 9 | close all 10 | clc 11 | 12 | % Add the path to the 'source_code' directory 13 | % The function genpath() is very convenient 14 | addpath(genpath('../../source_code')) 15 | 16 | % Set a project name (mfilename corresponds to the script name) 17 | project_name = mfilename; 18 | 19 | % Create a directory to store the figures and results 20 | results_path = fullfile(pwd, [project_name, '_results']); 21 | if exist(results_path, 'dir') ~= 7 % 7 is a MATLAB's convention 22 | mkdir(results_path) 23 | else 24 | rmdir(results_path, 's') 25 | mkdir(results_path) 26 | end 27 | 28 | 29 | %% Define the thermodynamic specifications and cycle parameters 30 | % Ambient state 31 | p_0 = 101325; % Ambient pressure 32 | T_0 = 30+273.15; % Ambient temperature 33 | 34 | % Define the working fluids 35 | heating_fluid = 'HEOS::Air'; 36 | working_fluid = 'HEOS::CO2'; 37 | cooling_fluid = 'HEOS::Water'; 38 | 39 | % Some guidance to select the fluid name and EoS 40 | %{ 41 | 42 | You can set the back-end used to compute the thermodynamic properties by 43 | precedding the fluid name by 'HEOS::' (for the Coolprop back-end) or by 44 | 'REFPROP::' (for the REFPROP backend). 45 | 46 | You can check the list of the CoolProp back-end fluid names here: 47 | http://www.coolprop.org/fluid_properties/PurePseudoPure.html 48 | 49 | Note that the fluid names for REFPROP may be different 50 | 51 | The equations of state may fail for some thermodynamic states and break 52 | down the optimization progress (oh, the horror...) 53 | 54 | This is depedent on the fluid and the EoS back-end (REFPROP/CoolProp) 55 | 56 | My recommendation is to try the CoolProp back-end first 57 | 1) CoolProp EoS are (much) faster, but can fail sometimes 58 | 2) REFPROP EoS are slower, but they are also a bit more robust 59 | 60 | The worst kind of error that you can get while executing the code is 61 | that the equations of state fail. 62 | 63 | There is not much to do about it when it happens because it depends on the 64 | CoolProp/REFPROP libraries. 65 | 66 | If you encounter this problem you can try to start the optimization from a 67 | different initial guess, change the bounds/constraints of the optimization 68 | or, as a last measure, change the EoS back-to REFPROP. 69 | 70 | If all of these fail, you are out of luck... 71 | 72 | %} 73 | 74 | % Define the thermodynamic specifications of the heat source 75 | m_h = 1.00; % Heat source mass flow rate (kg/s). This variable "scales-up" the problem 76 | T_1 = 400+273.15; % Heat source inlet temperature (K) 77 | p_1 = p_0; % Heat source inlet pressure (Pa) 78 | p_3 = p_0; % Heat source outlet pressure (Pa) 79 | T_3min = 50+273.15; % Minimum temperature at the outlet of the heat source (K) 80 | T_3max = T_1; % Maximum temperature at the outlet of the heat source (K) 81 | 82 | % Define the thermodynamic specifications of the heat sink 83 | T_10 = T_0; % Heat sink inlet temperature (K) 84 | p_10 = p_0; % Heat sink inlet pressure (Pa) 85 | p_12 = p_0; % Heat sink outlet pressure (Pa) 86 | T_12min = T_0+10; % Minimum temperature at the outlet of the heat sink (K) 87 | T_12max = T_0+20; % Maximum temperature at the outlet of the heat sink (K) 88 | 89 | % Define pressure drops in the heat exchangers (% of the inlet pressure) 90 | dp_h_evap = 0.02; % Pressure drop in the heating fluid side of the evaporator (relative with respect to inlet) 91 | dp_c_evap = 0.02; % Pressure drop in the working fluid side of the evaporator (relative with respect to inlet) 92 | dp_h_cond = 0.02; % Pressure drop in the working fluid side of the condenser (relative with respect to inlet) 93 | dp_c_cond = 0.02; % Pressure drop in the cooling fluid side of the condenser (relative with respect to inlet) 94 | dp_h_rec = 0.02; % Pressure drop in the hot side of the recuperator (relative with respect to inlet) 95 | dp_c_rec = 0.02; % Pressure drop in the cold side of the recuperator (relative with respect to inlet) 96 | 97 | % Define the number of discretizations of the heat exchangers 98 | % More discretizations: more accurate pinch point and more execution time 99 | N_evap = 50; % Choose an integer 100 | N_cond = 50; % Choose an integer 101 | N_rec = 50; % Choose an integer 102 | 103 | % Define the efficiency of the pumps 104 | eta_pump_h = 0.85; % Isentropic efficiency of the heat source pump 105 | eta_pump_f = 0.85; % Isentropic efficiency of the working fluid pump 106 | eta_pump_c = 0.85; % Isentropic efficiency of the heat sink pump 107 | 108 | % Define the efficiency of the expander 109 | eta_expander = 0.90; % Efficiency of the expander 110 | 111 | % Turbomachinery efficiency definition 112 | pump_efficiency_definition = 'isentropic'; % Definition of the pump efficiency: 'isentropic' or 'polytropic' 113 | expander_efficiency_definition = 'isentropic'; % Definition of the expander efficiency: 'isentropic' or 'polytropic' 114 | 115 | % More details about the efficiency definition 116 | %{ 117 | 118 | If you set efficiency_definition='isentropic' the efficiency of the 119 | working fluid pump and expander are defined as: 120 | 121 | eta_pump = (h_out_s-h_in)/(h_out-h_in) 122 | eta_expander = (h_in-h_out)/(h_in-h_out_s) 123 | 124 | and the turbomachinery computations only involve the inlet and exit states 125 | 126 | If you set efficiency_definition='polytropic' the efficiency of the working 127 | fluid pump and expander are defined as: 128 | 129 | eta_pump = dh_s/dh = 1/d*dp/dh 130 | eta_expander = dh/dh_s = d*dh/dp 131 | 132 | and the turbomachinery computations are performed by solving the previous 133 | ordinary differential equations (ODE) along the compression or expansion 134 | 135 | %} 136 | 137 | 138 | %% Define extra fluid parameters 139 | % Used to scale degrees of freedom (no need to do changes in most cases) 140 | % Define the critical properties 141 | p_crit = prop_calculation('P_CRITICAL',working_fluid); % Critical pressure 142 | T_crit = prop_calculation('T_CRITICAL',working_fluid); % Critical temperature 143 | % p_crit = []; 144 | % T_crit = []; 145 | 146 | % Define the triple properties (only pure substances) 147 | p_trip = prop_calculation('P_TRIPLE',working_fluid); % Triple pressure 148 | T_trip = prop_calculation('T_TRIPLE',working_fluid); % Triple temperature 149 | % p_trip = []; 150 | % T_trip = []; 151 | 152 | % Define the saturation pressure and enthalpy at ambient temperature 153 | % If the ambient temperature is higher than the critical temperature use 154 | % the critical pressure and enthalpy instead 155 | if T_0 < T_crit 156 | p_sat0 = prop_calculation('P','T',T_0,'Q',0,working_fluid); 157 | h_sat0 = prop_calculation('H','T',T_0,'Q',0,working_fluid); 158 | else 159 | p_sat0 = p_crit; 160 | h_sat0 = prop_calculation('H','T',T_crit,'P',p_crit,working_fluid); 161 | end 162 | 163 | % Define the pressure and temperature limits 164 | p_min = 1.01*p_trip; % Minimum pressure 165 | p_max = 5.00*p_crit; % Maximum pressure 166 | T_min = T_0; % Maximum temperature 167 | T_max = 1.50*T_crit; % Maximum temperature 168 | 169 | % Define the enthalpy limits 170 | h_min = h_sat0; 171 | h_max = prop_calculation('H','T',T_1,'P',1e-3,working_fluid); 172 | 173 | 174 | %% Define the independent variables and bounds 175 | % x1 = Heat source exit temperature 176 | x1 = 0.25; 177 | x1_min = 0.00; 178 | x1_max = 1.00; 179 | 180 | % x2 = Heat sink exit temperature 181 | x2 = 0.25; 182 | x2_min = 0.00; 183 | x2_max = 1.00; 184 | 185 | % x3 = Pump inlet pressure (subcritical) 186 | x3 = (2.0*p_sat0-p_min)/(p_max-p_min); 187 | x3_min = 0.00; 188 | x3_max = 1.00; 189 | 190 | % x4 = Pump inlet enthalpy 191 | x4 = 0.05; 192 | x4_min = 0.00; 193 | x4_max = 1.00; 194 | 195 | % x5 = Expander inlet pressure 196 | x5 = (3.00*p_crit-p_min)/(p_max-p_min); 197 | x5_min = 0.00; 198 | x5_max = 1.00; 199 | 200 | % x6 = Expander inlet enthalpy 201 | x6 = 0.80; 202 | x6_min = 0.00; 203 | x6_max = 1.00; 204 | 205 | % x7 = Recuperator effectiveness (set to zero for no recuperator) 206 | x7 = 0.25; 207 | x7_min = 0.00; 208 | x7_max = 1.00; 209 | 210 | % Organize the information in vectors 211 | lb_cycle = [x1_min, x2_min, x3_min, x4_min, x5_min, x6_min, x7_min]'; 212 | ub_cycle = [x1_max, x2_max, x3_max, x4_max, x5_max, x6_max, x7_max]'; 213 | x0_cycle = [x1, x2, x3, x4, x5, x6, x7]'; 214 | 215 | % Some guidelines for the degrees of freedom 216 | %{ 217 | 218 | Play around with x0_cycle to find a good initial guess. 219 | 220 | Mathematical definition of the degrees of freedom 221 | x1 = (T_3 - T_3min) / (T_3max - T_3min) 222 | x2 = (T_12 - T_12min) / (T_12max - T_12min) 223 | x3 = (p_4 - p_min) / (p_max - p_min) 224 | x4 = (h_4 - h_min) / (h_max - h_min) 225 | x5 = (p_7 - p_min) / (p_max - p_min) 226 | x6 = (h_7 - h_min) / (h_max - h_min) 227 | x7 = (h6 - h5) / (h(p6,T8) - h5) 228 | 229 | If you want a cycle configuration with no recuperator you have to: 230 | 1) Set the recuperator effectiveness equal to zero: 231 | x7 = x7_min = x7_max = 0.00 232 | 2) Set the pressure drops in the recuperator equal to zero: 233 | dp_h_rec = dp_c_rec = 0.00 234 | 3) Do not apply a constraint for the recuperator pinch point dT: 235 | constraints.dT_rec.apply = 'no' 236 | 237 | How to give start values when you use the code for the first time? 238 | x1 usually takes low values (maximum exploitation of the heat source) 239 | x2 usually takes low values (small temperature jump in the heat sink) 240 | x3 usually takes low values (low pressure at the exit of the condenser) 241 | x4 usually takes low values (low enthalpy at the exit of the condenser) 242 | x5x_crit for transcritical cycles 243 | x6 usually takes high values (high enthalpy at the inlet of the evap.) 244 | x7=0 corresponds to no recuperation and x=1 to ideal recuperation 245 | 246 | Note: Low means close to zero and high means close to one 247 | 248 | %} 249 | 250 | 251 | %% Define the constraints 252 | % Instructions: 253 | % 1. Specify the minimum and maximum values 254 | % 2. Specify whether to apply the constraint or not 255 | % 3. Use [brackets] to ignore the constraint value 256 | 257 | % Minimum temperature difference in the evaporator (pinch point) 258 | constraints.dT_evap.min = 10; 259 | constraints.dT_evap.apply = 'yes'; 260 | 261 | % Minimum temperature difference in the condenser (pinch point) 262 | constraints.dT_cond.min = 10; 263 | constraints.dT_cond.apply = 'yes'; 264 | 265 | % Minimum temperature difference in the recuperator (pinch point) 266 | constraints.dT_rec.min = 10; 267 | constraints.dT_rec.apply = 'yes'; 268 | 269 | % Minimum vapor quality along the expander 270 | constraints.quality.min = 1.00; 271 | constraints.quality.apply = 'yes'; 272 | 273 | % Degree of subcooling at the inlet of the pump 274 | constraints.dT_subcooling.min = 1; 275 | constraints.dT_subcooling.max = []; 276 | constraints.dT_subcooling.apply = 'no'; 277 | 278 | % Degree of superheating at inlet of the expander (subcritical cycles) 279 | constraints.dT_superheating.min = 5; 280 | constraints.dT_superheating.max = []; 281 | constraints.dT_superheating.apply = 'no'; 282 | 283 | % Maximum cycle pressure 284 | constraints.pressure.min = []; 285 | constraints.pressure.max = 300e5; 286 | constraints.pressure.apply = 'no'; 287 | 288 | 289 | %% Define optimization algorithm settings 290 | % Set optimization algorithm 291 | % 'spq' is my favourite because it converges reliably and respects bounds 292 | % 'active-set' is a bit more aggressive and does not always respect the 293 | % bounds. It is faster, but less reliable, than 'sqp'. 294 | % 'interior-point' also works, but it requires more iterations 295 | algorithm = 'sqp'; 296 | 297 | % Compute the problem gradients in parallel or not 298 | use_parallel = false; % true or false 299 | 300 | % Define termination criteria (no need to change in most cases) 301 | max_iterations = 1000; 302 | max_function_evals = 10000; 303 | step_tolerance = 1e-08; 304 | function_tolerance = 1e-05; 305 | constraint_tolerance = 1e-05; 306 | optimality_tolerance = 1e-03; 307 | 308 | % Description of the possible outputs of the optimization function 309 | %{ 310 | 311 | 'interior-point', 'active-set', and 'sqp' algorithms 312 | Exitflag = 1 -> Success. First-order optimality measure was less than options and constraints are not violated. 313 | Exitflag = 2 -> Success. Step size was less than options and constraints are not violated. 314 | Exitflag = 0 -> Unsuccess. Number of iterations or function evaluations exceeded option 315 | Exitflag = -1 -> Unsuccess. Solver stopped by an output function 316 | Exitflag = -2 -> Unsuccess. No feasible point was found 317 | 318 | Only active set algorithm 319 | Exitflag = 4 -> Success. Magnitude of the search direction was less than 2*options.StepTolerance and maximum constraint violation was less than options.ConstraintTolerance. 320 | Exitflag = 5 -> Success. Magnitude of directional derivative in search direction was less than 2*options.OptimalityTolerance and maximum constraint violation was less than options.ConstraintTolerance. 321 | 322 | %} 323 | 324 | 325 | %% Choose what figures should be plotted 326 | choose_plots = struct('diagram_TQ', 'yes', ... % Temperature - heat duty diagram 327 | 'diagram_Ts', 'yes', ... % Temperature - entropy diagram of the cycle 328 | 'diagram_Th', 'yes', ... % Temperature - enthalpy diagram of the cycle 329 | 'save', 0); % Choose whether to save the figures or not 330 | 331 | % Description of the saving options 332 | %{ 333 | 334 | save=0 does not save the figures 335 | save=1 saves the figure in vector format using the default MATLAB export function (faster) 336 | save=2 saves the figure in vector using the export_fig library (requires ghostscript) 337 | 338 | In order to use save=2 it is necessary to install 'ghoscript' 339 | https://github.com/altmany/export_fig 340 | http://www.ghostscript.com 341 | http://xpdfreader.com 342 | 343 | %} 344 | 345 | 346 | %% Store the parameters defined up to this point into data structures 347 | % The script 'create_problem_structures' located in source_code folder 348 | % 1) 'fixed_parameters' stores the cycle specifications 349 | % 2) 'optimization_problem' stores the optimization problem settings 350 | create_problem_structures 351 | 352 | % These data structures are created in a different script to keep the 353 | % number of lines of this script to a minimum 354 | 355 | % Be tidy and clear the worskpace now that we stored everything 356 | clearvars -except optimization_problem fixed_parameters project_name results_path 357 | 358 | 359 | %% Load a previous solution as initial guess 360 | % % Uncomment these lines to use a previous solution as initial guess 361 | % load(fullfile(fixed_parameters.results_path,'cycle_data.mat')) 362 | % optimization_problem.x0 = cycle_data.optimization.x; 363 | 364 | 365 | %% Plot the initial solution 366 | filename_suffix = 'initial'; 367 | cycle_data = create_plots(optimization_problem.x0,fixed_parameters,filename_suffix); 368 | print_solution(cycle_data) 369 | 370 | 371 | %% Solve the optimization problem 372 | [cycle_data,x_opt,f_opt,exitflag,output,lambda] = solve_optimization_problem(fixed_parameters,optimization_problem); 373 | save_solution(cycle_data, project_name, results_path) 374 | 375 | %% Plot the optimal solution 376 | filename_suffix = 'optimal'; 377 | create_plots(x_opt,fixed_parameters,filename_suffix); 378 | print_solution(cycle_data) 379 | 380 | 381 | 382 | -------------------------------------------------------------------------------- /source_code/create_data_structure.m: -------------------------------------------------------------------------------- 1 | function cycle_data = create_data_structure 2 | 3 | % This function is not really necessary for the computations since it is 4 | % not necessary to declare variables in MATLAB 5 | % It can be used as a reference where the cycle variables are documented 6 | 7 | % States of the thermodynamic cycle 8 | cycle_states = struct('fluid', [], ... % Fluid name 9 | 'p', [], ... % Pressure [kPa] 10 | 'T', [], ... % Temperature [K] 11 | 'd', [], ... % Density [kg/m3] 12 | 'h', [], ... % Enthalpy [J/kg] 13 | 's', [], ... % Entropy [J/kg K] 14 | 'e', []); % Exergy [J/kg] 15 | 16 | cycle_states(1:7) = cycle_states; 17 | 18 | %Fluid properties 19 | properties = struct('p_0', [], ... % Ambient pressure 20 | 'T_0', [], ... % Ambient temperature 21 | 'p_crit', [], ... % Critical pressure 22 | 'T_crit', [], ... % Critical temperature 23 | 'p_trip', [], ... % Triple pressure 24 | 'T_trip', []); % Triple temperature 25 | 26 | % Working fluids 27 | fluids = []; 28 | 29 | 30 | % Mass flows 31 | mass_flows = struct('m_h', [], ... % Heating fluid mass flow rate [kg/s] 32 | 'm_f', [], ... % Working fluid mass flow rate [kg/s] 33 | 'm_c', []); % Cooling fluid mass flow rate [kg/s] 34 | 35 | 36 | % Energy flows 37 | energy_flows = struct('Q_max', [], ... % Heat flow rate if the heat source is cooled down to ambient temperature [W] 38 | 'Q_evap', [], ... % Heat flow rate absorbed in the heat exchanger [W] 39 | 'W_exp', [], ... % Power delivered by the expander [W] 40 | 'W_pump_h', [], ... % Power consumed by the pump [W] 41 | 'W_pump_f', [], ... % Power consumed by the pump [W] 42 | 'W_pump_c', []); % Power consumed by the pump [W] 43 | 44 | 45 | % % Exergy flows 46 | exergy_flows = struct('E_max', [], ... % Exergy flow if the heat source is cooled down to ambient temperature [W] 47 | 'I_evap', [], ... % Exergy destruction in the heat exchanger [W] 48 | 'I_cond', [], ... % Exergy destruction in the heat exchanger [W] 49 | 'I_rec', [], ... % Exergy destruction in the heat exchanger [W] 50 | 'I_pump_h', [], ... % Exergy destruction in the pump [W] 51 | 'I_pump_f', [], ... % Exergy destruction in the pump [W] 52 | 'I_pump_c', [], ... % Exergy destruction in the pump [W] 53 | 'I_exp', [], ... % Exergy destruction in the expander [W] 54 | 'I_source', [], ... % Exergy flow at the exit of the heat source [W] 55 | 'I_sink', []); % Exergy flow at the exit of the heat sink [W] 56 | 57 | 58 | % Efficiencies of the cycle 59 | efficiency = struct('eta_1', [], ... % Plant energy efficiency 60 | 'eta_1recovery', [], ... % Energy recovery efficiency 61 | 'eta_1cycle', [], ... % Cycle energy efficiency 62 | 'eta_2', [], ... % Plant exergy efficiency 63 | 'eta_2recovery', [], ... % Exergy recovery efficiency 64 | 'eta_2cycle', []); % Cycle exergy efficiency 65 | 66 | 67 | % Variables associated with the expander 68 | expander = struct('N', [], ... % Number of discretizations 69 | 'p', [], ... % Pressure [kPa] 70 | 'T', [], ... % Temperature [K] 71 | 'd', [], ... % Density [kg/m3] 72 | 'q', [], ... % Quality [-] 73 | 'h', [], ... % Enthalpy [J/kg] 74 | 's', [], ... % Entropy [J/kg K] 75 | 'eta', []); % Polytropic efficiency 76 | 77 | % Variables associated with the pump 78 | pump = struct('N', [], ... % Number of discretizations 79 | 'p', [], ... % Pressure [kPa] 80 | 'T', [], ... % Temperature [K] 81 | 'd', [], ... % Density [kg/m3] 82 | 'q', [], ... % Quality [-] 83 | 'h', [], ... % Enthalpy [J/kg] 84 | 's', [], ... % Entropy [J/kg K] 85 | 'eta', []); % Polytropic efficiency 86 | 87 | 88 | % Variables associated with the heat exchanger 89 | exchanger = struct('N', [], ... % Number of discretizations 90 | 'p_h', [], ... % Pressure [kPa] 91 | 'p_c', [], ... % Pressure [kPa] 92 | 'T_h', [], ... % Temperature [K] 93 | 'T_c', [], ... % Temperature [K] 94 | 'd_h', [], ... % Density [kg/m3] 95 | 'd_c', [], ... % Density [kg/m3] 96 | 'h_h', [], ... % Enthalpy [J/kg] 97 | 'h_c', [], ... % Enthalpy [J/kg] 98 | 's_h', [], ... % Entropy [J/kg K] 99 | 's_c', [], ... % Entropy [J/kg K] 100 | 'dT_min', []); % Pinch point [K] 101 | 102 | 103 | % Variables associated with the optimization problem 104 | optimization = struct('x', [], ... % Vector of degrees of freedom 105 | 'lb', [], ... % Vector of upper bounds 106 | 'ub', [], ... % Vector of lower bounds 107 | 'c', [], ... % Vector of inequality constraints 108 | 'c_eq', []); % Vector of equality constraints 109 | 110 | 111 | 112 | % Create the cycle_data structure grouping the sub-structures 113 | cycle_data = struct('cycle_states', cycle_states, ... 114 | 'properties', properties, ... 115 | 'fluids', fluids, ... 116 | 'mass_flows', mass_flows, ... 117 | 'energy_flows', energy_flows, ... 118 | 'exergy_flows', exergy_flows, ... 119 | 'efficiency', efficiency, ... 120 | 'evaporator', exchanger, ... 121 | 'condenser', exchanger, ... 122 | 'recuperator', exchanger, ... 123 | 'expander', expander, ... 124 | 'pump_h', pump, ... 125 | 'pump_f', pump, ... 126 | 'pump_c', pump, ... 127 | 'optimization', optimization); 128 | 129 | 130 | end 131 | -------------------------------------------------------------------------------- /source_code/create_problem_structures.m: -------------------------------------------------------------------------------- 1 | %% Save the cycle parameters as a structure 2 | % Simple script to organize information into an structure 3 | 4 | % Thermodynamic boundaries 5 | boundary_conditions = struct('heating_fluid', {heating_fluid}, ... 6 | 'working_fluid', {working_fluid}, ... 7 | 'cooling_fluid', {cooling_fluid}, ... 8 | 'm_h', m_h, ... 9 | 'T_1', T_1, ... 10 | 'p_1', p_1, ... 11 | 'p_3', p_3, ... 12 | 'T_3min', T_3min, ... 13 | 'T_3max', T_3max, ... 14 | 'T_10', T_10, ... 15 | 'p_10', p_10, ... 16 | 'p_12', p_12, ... 17 | 'T_12min', T_12min, ... 18 | 'T_12max', T_12max); 19 | 20 | % Fluid properties 21 | properties = struct('p_0', p_0, ... 22 | 'T_0', T_0, ... 23 | 'p_crit', p_crit, ... 24 | 'T_crit', T_crit, ... 25 | 'p_trip', p_trip, ... 26 | 'T_trip', T_trip, ... 27 | 'p_max', p_max, ... 28 | 'T_max', T_max, ... 29 | 'h_max', h_max, ... 30 | 'p_min', p_min, ... 31 | 'T_min', T_min, ... 32 | 'h_min', h_min); 33 | 34 | % Component specifications 35 | components = struct('dp_h_evap', dp_h_evap, ... 36 | 'dp_c_evap', dp_c_evap, ... 37 | 'dp_h_cond', dp_h_cond, ... 38 | 'dp_c_cond', dp_c_cond, ... 39 | 'dp_h_rec', dp_h_rec, ... 40 | 'dp_c_rec', dp_c_rec, ... 41 | 'N_evap', N_evap, ... 42 | 'N_cond', N_cond, ... 43 | 'N_rec', N_rec, ... 44 | 'eta_pump_h', eta_pump_h, ... 45 | 'eta_pump_f', eta_pump_f, ... 46 | 'eta_pump_c', eta_pump_c, ... 47 | 'eta_expander', eta_expander, ... 48 | 'pump_efficiency_definition', pump_efficiency_definition, ... 49 | 'expander_efficiency_definition', expander_efficiency_definition); 50 | 51 | % Lower bounds for the degrees of freedom 52 | lower_bounds = struct('x1', x1_min, ... 53 | 'x2', x2_min, ... 54 | 'x3', x3_min, ... 55 | 'x4', x4_min, ... 56 | 'x5', x5_min, ... 57 | 'x6', x6_min, ... 58 | 'x7', x7_min); 59 | 60 | % Upper bounds for the degrees of freedom 61 | upper_bounds = struct('x1', x1_max, ... 62 | 'x2', x2_max, ... 63 | 'x3', x3_max, ... 64 | 'x4', x4_max, ... 65 | 'x5', x5_max, ... 66 | 'x6', x6_max, ... 67 | 'x7', x7_max); 68 | 69 | % Organize the previous structures in a compact way 70 | fixed_parameters = struct('boundary_conditions', boundary_conditions, ... 71 | 'properties', properties, ... 72 | 'components', components, ... 73 | 'lower_bounds', lower_bounds, ... 74 | 'upper_bounds', upper_bounds, ... 75 | 'constraints', constraints, ... 76 | 'results_path', results_path, ... 77 | 'project_name', project_name, ... 78 | 'choose_plots', choose_plots); 79 | 80 | 81 | %% Create optimization_problem structure 82 | optimization_problem = struct('lb', [], ... % Vector of lower bounds 83 | 'ub', [], ... % Vector of upper bounds 84 | 'Aeq', [], ... % Matrix for linear equality constraints 85 | 'beq', [], ... % Vector for linear equality constraints 86 | 'Aineq', [], ... % Matrix for linear inequality constraints 87 | 'bineq', [], ... % Vector for linear inequality constraints 88 | 'nonlcon', [], ... % Nonlinear constraints function 89 | 'objective', [], ... % Objective function 90 | 'x0', [], ... % Initial guess for the degrees of freedom 91 | 'options', [], ... % Optimization options structure 92 | 'solver', 'fmincon'); 93 | 94 | % Define the initial guess vector 95 | optimization_problem.x0 = x0_cycle; 96 | 97 | % Define the vector of lower bounds 98 | optimization_problem.lb = lb_cycle; 99 | 100 | % Define the vector of upper bounds 101 | optimization_problem.ub = ub_cycle; 102 | 103 | % Define the options using the optimoptions function 104 | % Change the type of algorithm here 105 | optimization_problem.options = optimoptions(@fmincon, ... 106 | 'Display', 'iter-detailed', ... 107 | 'Algorithm', algorithm, ... 108 | 'StepTolerance', step_tolerance, ... 109 | 'ConstraintTolerance', constraint_tolerance, ... 110 | 'OptimalityTolerance', optimality_tolerance, ... 111 | 'FunctionTolerance', function_tolerance, ... 112 | 'MaxFunctionEvaluations', max_function_evals, ... 113 | 'MaxIterations', max_iterations, ... 114 | 'FiniteDifferenceStepSize', sqrt(eps), ... 115 | 'UseParallel', use_parallel, ... 116 | 'PlotFcn', {@optimplotfval, ... 117 | @optimplotconstrviolation, ... 118 | @optimplotfirstorderopt, ... 119 | @optimplotstepsize}); 120 | 121 | 122 | optimization_problem.options.OutputFcn = @(x,optimValues,state)save_current_solution(x,optimValues,state,fixed_parameters); 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /source_code/evaluate_compressor.m: -------------------------------------------------------------------------------- 1 | function [h_high,p,h,T,s,d] = evaluate_compressor(fluid,h_low,p_low,p_high,efficiency,efficiency_definition,calc_detail) 2 | 3 | if strcmp(efficiency_definition,'isentropic') == 1 4 | 5 | % Isentropic efficiency computation 6 | s = prop_calculation('S','P',p_low,'H',h_low,fluid); 7 | h_high_s = prop_calculation('H','P',p_high,'S',s,fluid); 8 | h_high = h_low+(h_high_s-h_low)/efficiency; 9 | p = [p_low p_high]'; 10 | h = [h_low h_high]'; 11 | 12 | elseif strcmp(efficiency_definition,'polytropic') == 1 13 | 14 | % Polytropic efficiency computation 15 | options = odeset('RelTol',1e-6,'AbsTol',1e-6); 16 | [p,h] = ode45(@(p,h)R(p,h,fluid,efficiency),linspace(p_low,p_high,25),h_low,options); 17 | h_high = h(end); 18 | 19 | else 20 | 21 | error("The efficiency definition must be 'isentropic' or 'polytropic'") 22 | 23 | end 24 | 25 | % Compute the other thermodynamic variables 26 | if strcmp(calc_detail,'short') 27 | T = 0*p; s = 0*p; d = 0*p; 28 | elseif strcmp(calc_detail,'long') 29 | T = 0*p; s = 0*p; d = 0*p; 30 | for i = 1:length(p) 31 | T(i) = prop_calculation('T','P',p(i),'H',h(i),fluid); 32 | s(i) = prop_calculation('S','P',p(i),'H',h(i),fluid); 33 | d(i) = prop_calculation('D','P',p(i),'H',h(i),fluid); 34 | end 35 | end 36 | 37 | end 38 | 39 | function dhdp = R(p,h,fluid,efficiency) 40 | rho = prop_calculation('D','P',p,'H',h,fluid); 41 | dhdp = 1/efficiency/rho; 42 | end 43 | 44 | 45 | % N = 10; 46 | % dp = (p_high-p_low)/(N-1); 47 | % p = (p_low:dp:p_high)'; 48 | % h = zeros(N,1); 49 | % h(1) = h_low; 50 | % for n = 1:N-1 51 | % h(n+1) = h(n)+dp*R(p(n),h(n),fluid,eta_poly); 52 | % end 53 | % h_high = h(end); -------------------------------------------------------------------------------- /source_code/evaluate_exchanger.m: -------------------------------------------------------------------------------- 1 | function [dT,T_h,T_c,h_h,h_c,p_h,p_c,d_h,d_c,s_h,s_c] = evaluate_exchanger(fluid_h,fluid_c,m_h,m_c,p_h1,p_h2,p_c1,p_c2,h_h1,h_h2,h_c1,h_c2,N,calc_detail) 2 | %% Preliminary computations 3 | 4 | % Compute the mass flow rate ratio 5 | m = m_h/m_c; 6 | 7 | % Compute the temperature difference at the inlet 8 | T_h1 = prop_calculation('T','P',p_h1,'H',h_h1,fluid_h); 9 | T_c1 = prop_calculation('T','P',p_c1,'H',h_c1,fluid_c); 10 | 11 | 12 | %% Discretize the heat exchanger in N nodes 13 | % Discretize the heat exchanger in N nodes 14 | dq = (h_h2-h_h1)/(N-1); % Heat exchange step 15 | dp_h = (p_h2-p_h1)/(N-1); % Hot pressure drop step 16 | dp_c = (p_c2-p_c1)/(N-1); % Cold pressure drop step 17 | 18 | % Preallocate memory 19 | h_h = zeros(N,1); h_c = zeros(N,1); 20 | T_h = zeros(N,1); T_c = zeros(N,1); 21 | p_h = zeros(N,1); p_c = zeros(N,1); 22 | 23 | % Initialize variables 24 | h_h(1) = h_h1; h_c(1) = h_c1; 25 | T_h(1) = T_h1; T_c(1) = T_c1; 26 | p_h(1) = p_h1; p_c(1) = p_c1; 27 | 28 | % Main loop 29 | for i = 1:N-1 30 | h_h(i+1) = h_h(i)+dq; 31 | h_c(i+1) = h_c(i)+dq*m; 32 | p_h(i+1) = p_h(i)+dp_h; 33 | p_c(i+1) = p_c(i)+dp_c; 34 | T_h(i+1) = prop_calculation('T','P',p_h(i+1),'H',h_h(i+1),fluid_h); 35 | T_c(i+1) = prop_calculation('T','P',p_c(i+1),'H',h_c(i+1),fluid_c); 36 | % q_h(i+1) = prop_calculation('Q','P',p_h(i+1),'H',h_h(i+1),fluid_h); 37 | % q_c(i+1) = prop_calculation('Q','P',p_c(i+1),'H',h_c(i+1),fluid_c); 38 | end 39 | 40 | % I am not satisfied with the solution adding extra nodes at phase changes 41 | % The linear pressure drop with heat transfer is kept to have a close 42 | % thermodynamic diagram when plotting 43 | % I think that the extra programming complexity and loss of robustness is 44 | % not worth the extra accuracy at the phase changes 45 | % If an accurate cycle simulation is desired the number of the 46 | % discretizations can be increased after a local optima has been found 47 | 48 | 49 | %% Compute the other thermodynamic variables 50 | if strcmp(calc_detail,'short') 51 | d_h = zeros(N,1); d_c = zeros(N,1); s_h = zeros(N,1); s_c = zeros(N,1); 52 | elseif strcmp(calc_detail,'long') 53 | d_h = zeros(N,1); d_c = zeros(N,1); s_h = zeros(N,1); s_c = zeros(N,1); 54 | for i = 1:N 55 | d_h(i) = prop_calculation('D','P',p_h(i),'H',h_h(i),fluid_h); 56 | d_c(i) = prop_calculation('D','P',p_c(i),'H',h_c(i),fluid_c); 57 | s_h(i) = prop_calculation('S','P',p_h(i),'H',h_h(i),fluid_h); 58 | s_c(i) = prop_calculation('S','P',p_c(i),'H',h_c(i),fluid_c); 59 | end 60 | end 61 | 62 | 63 | %% Compute the pinch point temperature difference 64 | dT = T_h-T_c; 65 | % dT_min = min(dT); 66 | 67 | 68 | end 69 | -------------------------------------------------------------------------------- /source_code/evaluate_expander.m: -------------------------------------------------------------------------------- 1 | function [h_low,p,h,T,s,d] = evaluate_expander(fluid,h_high,p_high,p_low,efficiency,efficiency_definition,calc_detail) 2 | 3 | if strcmp(efficiency_definition,'isentropic') == 1 4 | 5 | % Isentropic efficiency computation 6 | s = prop_calculation('S','P',p_high,'H',h_high,fluid); 7 | h_low_s = prop_calculation('H','P',p_low,'S',s,fluid); 8 | h_low = h_high-(h_high-h_low_s)*efficiency; 9 | p = [p_low p_high]'; 10 | h = [h_low h_high]'; 11 | 12 | elseif strcmp(efficiency_definition,'polytropic') == 1 13 | 14 | % Polytropic efficiency computation 15 | options = odeset('RelTol',1e-6,'AbsTol',1e-6); 16 | [p,h] = ode45(@(p,h)R(p,h,fluid,efficiency),linspace(p_high,p_low,25),h_high,options); 17 | h_low = h(end); 18 | 19 | else 20 | 21 | error("The efficiency definition must be 'isentropic' or 'polytropic'") 22 | 23 | end 24 | 25 | % Compute the other thermodynamic variables 26 | if strcmp(calc_detail,'short') 27 | T = 0*p; s = 0*p; d = 0*p; 28 | elseif strcmp(calc_detail,'long') 29 | T = 0*p; s = 0*p; d = 0*p; 30 | for i = 1:length(p) 31 | T(i) = prop_calculation('T','P',p(i),'H',h(i),fluid); 32 | s(i) = prop_calculation('S','P',p(i),'H',h(i),fluid); 33 | d(i) = prop_calculation('D','P',p(i),'H',h(i),fluid); 34 | end 35 | end 36 | 37 | end 38 | 39 | function dhdp = R(p,h,fluid,efficiency) 40 | rho = prop_calculation('D','P',p,'H',h,fluid); 41 | dhdp = efficiency/rho; 42 | end 43 | 44 | % N = 10; 45 | % dp = (p_low-p_high)/(N-1); 46 | % p = (p_high:dp:p_low)'; 47 | % h = zeros(N,1); 48 | % h(1) = h_high; 49 | % for n = 1:N-1 50 | % h(n+1) = h(n)+dp*R(p(n),h(n),fluid,eta_poly); 51 | % end 52 | % h_low = h(end); -------------------------------------------------------------------------------- /source_code/evaluate_optimization_problem.m: -------------------------------------------------------------------------------- 1 | function [cycle_data, f, c, c_eq] = evaluate_optimization_problem(x,fixed_parameters) 2 | 3 | % Evaluate the thermodynamic cycle model 4 | cycle_data = evaluate_rankine_cycle(x,fixed_parameters); 5 | 6 | % Extract the objective function value 7 | f = -[cycle_data.efficiency.eta_1]; 8 | % f = -[cycle_data.efficiency.eta_2]; 9 | 10 | % Extract the vector of inequality constraints 11 | c = cycle_data.optimization.c; 12 | 13 | % Extract the vector of equality constraints 14 | c_eq = cycle_data.optimization.c_eq; 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /source_code/export_fig/.gitignore: -------------------------------------------------------------------------------- 1 | /.ignore 2 | *.txt 3 | *.asv 4 | *~ 5 | *.mex* 6 | -------------------------------------------------------------------------------- /source_code/export_fig/Export_fig.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /source_code/export_fig/ImageSelection.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turbo-sim/RankineLab/f09b7ee6ddc17f0987951fc74d79dc2c02faf829/source_code/export_fig/ImageSelection.class -------------------------------------------------------------------------------- /source_code/export_fig/ImageSelection.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/turbo-sim/RankineLab/f09b7ee6ddc17f0987951fc74d79dc2c02faf829/source_code/export_fig/ImageSelection.java -------------------------------------------------------------------------------- /source_code/export_fig/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Oliver J. Woodford, Yair M. Altman 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /source_code/export_fig/append_pdfs.m: -------------------------------------------------------------------------------- 1 | function append_pdfs(varargin) 2 | %APPEND_PDFS Appends/concatenates multiple PDF files 3 | % 4 | % Example: 5 | % append_pdfs(outputFilename, inputFilename1, inputFilename2, ...) 6 | % append_pdfs(outputFilename, inputFilenames_list{:}) 7 | % append_pdfs(outputFilename, inputFilenames_cell_or_string_array) 8 | % append_pdfs output.pdf input1.pdf input2.pdf 9 | % 10 | % This function appends multiple PDF files to an existing PDF file, or 11 | % concatenates them into a PDF file if the output file doesn't yet exist. 12 | % 13 | % This function requires that you have ghostscript installed on your 14 | % system. Ghostscript can be downloaded from: http://www.ghostscript.com 15 | % 16 | % IN: 17 | % output - string of output file name (including the extension, .pdf). 18 | % If it exists it is appended to; if not, it is created. 19 | % input1 - string of an input file name (including the extension, .pdf). 20 | % All input files are appended in order. 21 | % input_list - cell array list of input file name strings. All input 22 | % files are appended in order. 23 | 24 | % Copyright: Oliver Woodford, 2011-2014, Yair Altman 2015- 25 | 26 | %{ 27 | % Thanks to Reinhard Knoll for pointing out that appending multiple pdfs in 28 | % one go is much faster than appending them one at a time. 29 | 30 | % Thanks to Michael Teo for reporting the issue of a too long command line. 31 | % Issue resolved on 5/5/2011, by passing gs a command file. 32 | 33 | % Thanks to Martin Wittmann for pointing out quality issue when appending bitmaps 34 | % Issue resolved (to best of my ability) 1/6/2011, using the prepress setting 35 | 36 | % 26/02/15: If temp dir is not writable, use the output folder for temp 37 | % files when appending (Javier Paredes); sanity check of inputs 38 | % 24/01/18: Fixed error in case of existing output file (append mode) 39 | % 24/01/18: Fixed issue #213: non-ASCII characters in folder names on Windows 40 | % 06/12/18: Avoid an "invalid escape-char" warning upon error 41 | % 22/03/20: Alert if ghostscript.m is not found on Matlab path 42 | % 29/03/20: Accept a cell-array of input files (issue #299); accept both "strings", 'chars' 43 | %} 44 | 45 | if nargin < 2, return; end % sanity check 46 | 47 | % Convert strings => chars; strtrim extra spaces 48 | varargin = cellfun(@str2char,varargin,'un',false); 49 | 50 | % Convert cell array into individual strings (issue #299) 51 | if nargin==2 && iscell(varargin{2}) 52 | varargin = {varargin{1} varargin{2}{:}}; %#ok 53 | end 54 | 55 | % Ensure that ghostscript() exists on the Matlab path 56 | if ~exist('ghostscript','file') 57 | error('export_fig:append_pdfs:ghostscript', 'The ghostscript.m function is required by append_pdf.m. Install the complete export_fig package from https://www.mathworks.com/matlabcentral/fileexchange/23629-export_fig or https://github.com/altmany/export_fig') 58 | end 59 | 60 | % Are we appending or creating a new file 61 | append = exist(varargin{1}, 'file') == 2; 62 | output = [tempname '.pdf']; 63 | try 64 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 65 | fid = fopen(output,'w'); 66 | fwrite(fid,1); 67 | fclose(fid); 68 | delete(output); 69 | isTempDirOk = true; 70 | catch 71 | % Temp dir is not writable, so use the output folder 72 | [dummy,fname,fext] = fileparts(output); %#ok 73 | fpath = fileparts(varargin{1}); 74 | output = fullfile(fpath,[fname fext]); 75 | isTempDirOk = false; 76 | end 77 | if ~append 78 | output = varargin{1}; 79 | varargin = varargin(2:end); 80 | end 81 | 82 | % Create the command file 83 | if isTempDirOk 84 | cmdfile = [tempname '.txt']; 85 | else 86 | cmdfile = fullfile(fpath,[fname '.txt']); 87 | end 88 | prepareCmdFile(cmdfile, output, varargin{:}); 89 | 90 | % Call ghostscript 91 | [status, errMsg] = ghostscript(['@"' cmdfile '"']); 92 | 93 | % Check for ghostscript execution errors 94 | if status && ~isempty(strfind(errMsg,'undefinedfile')) && ispc %#ok 95 | % Fix issue #213: non-ASCII characters in folder names on Windows 96 | for fileIdx = 2 : numel(varargin) 97 | [fpath,fname,fext] = fileparts(varargin{fileIdx}); 98 | varargin{fileIdx} = fullfile(normalizePath(fpath),[fname fext]); 99 | end 100 | % Rerun ghostscript with the normalized folder names 101 | prepareCmdFile(cmdfile, output, varargin{:}); 102 | [status, errMsg] = ghostscript(['@"' cmdfile '"']); 103 | end 104 | 105 | % Delete the command file 106 | delete(cmdfile); 107 | 108 | % Check for ghostscript execution errors 109 | if status 110 | errMsg = strrep(errMsg,'\','\\'); % Avoid an "invalid escape-char" warning 111 | error('YMA:export_fig:append_pdf',errMsg); 112 | end 113 | 114 | % Rename the file if needed 115 | if append 116 | movefile(output, varargin{1}, 'f'); 117 | end 118 | end 119 | 120 | % Prepare a text file with ghostscript directives 121 | function prepareCmdFile(cmdfile, output, varargin) 122 | fh = fopen(cmdfile, 'w'); 123 | fprintf(fh, '-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile="%s" -f', output); 124 | fprintf(fh, ' "%s"', varargin{:}); 125 | fclose(fh); 126 | end 127 | 128 | % Convert long/non-ASCII folder names into their short ASCII equivalents 129 | function pathStr = normalizePath(pathStr) 130 | [fpath,fname,fext] = fileparts(pathStr); 131 | if isempty(fpath) || strcmpi(fpath,pathStr), return, end 132 | dirOutput = evalc(['system(''dir /X /AD "' pathStr '*"'')']); 133 | shortName = strtrim(regexprep(dirOutput,{'.*> *',[fname fext '.*']},'')); 134 | if isempty(shortName) 135 | shortName = [fname fext]; 136 | end 137 | fpath = normalizePath(fpath); %recursive until entire fpath is processed 138 | pathStr = fullfile(fpath, shortName); 139 | end 140 | 141 | % Convert a possible string => char 142 | function value = str2char(value) 143 | try 144 | value = controllib.internal.util.hString2Char(value); 145 | catch 146 | if isa(value,'string') 147 | value = char(value); 148 | end 149 | end 150 | value = strtrim(value); 151 | end 152 | -------------------------------------------------------------------------------- /source_code/export_fig/copyfig.m: -------------------------------------------------------------------------------- 1 | function fh = copyfig(fh) 2 | %COPYFIG Create a copy of a figure, without changing the figure 3 | % 4 | % Examples: 5 | % fh_new = copyfig(fh_old) 6 | % 7 | % This function will create a copy of a figure, but not change the figure, 8 | % as copyobj sometimes does, e.g. by changing legends. 9 | % 10 | % IN: 11 | % fh_old - The handle of the figure to be copied. Default: gcf. 12 | % 13 | % OUT: 14 | % fh_new - The handle of the created figure. 15 | 16 | % Copyright (C) Oliver Woodford 2012, Yair Altman 2015 17 | 18 | % 26/02/15: If temp dir is not writable, use the dest folder for temp 19 | % destination files (Javier Paredes) 20 | % 15/04/15: Suppress warnings during copyobj (Dun Kirk comment on FEX page 2013-10-02) 21 | % 09/09/18: Fix issue #252: Workaround for cases where copyobj() fails for any reason 22 | 23 | % Set the default 24 | if nargin == 0 25 | fh = gcf; 26 | end 27 | % Is there a legend? 28 | useCopyobj = isempty(findall(fh, 'Type', 'axes', 'Tag', 'legend')); 29 | if useCopyobj 30 | % Safe to copy using copyobj 31 | oldWarn = warning('off'); %Suppress warnings during copyobj (Dun Kirk comment on FEX page 2013-10-02) 32 | try 33 | fh = copyobj(fh, 0); 34 | catch 35 | % Fix issue #252: Workaround for cases where copyobj() fails for any reason 36 | useCopyobj = false; % if copyobj() croaks, use file save/load below 37 | end 38 | warning(oldWarn); 39 | end 40 | if ~useCopyobj 41 | % copyobj will change the figure, so save and then load it instead 42 | tmp_nam = [tempname '.fig']; 43 | try 44 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 45 | fid = fopen(tmp_nam,'w'); 46 | fwrite(fid,1); 47 | fclose(fid); 48 | delete(tmp_nam); % cleanup 49 | catch 50 | % Temp dir is not writable, so use the current folder 51 | [dummy,fname,fext] = fileparts(tmp_nam); %#ok 52 | fpath = pwd; 53 | tmp_nam = fullfile(fpath,[fname fext]); 54 | end 55 | hgsave(fh, tmp_nam); 56 | fh = hgload(tmp_nam); 57 | delete(tmp_nam); 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /source_code/export_fig/crop_borders.m: -------------------------------------------------------------------------------- 1 | function [A, vA, vB, bb_rel] = crop_borders(A, bcol, padding, crop_amounts) 2 | %CROP_BORDERS Crop the borders of an image or stack of images 3 | % 4 | % [B, vA, vB, bb_rel] = crop_borders(A, bcol, [padding]) 5 | % 6 | %IN: 7 | % A - HxWxCxN stack of images. 8 | % bcol - Cx1 background colour vector. 9 | % padding - scalar indicating how much padding to have in relation to 10 | % the cropped-image-size (0<=padding<=1). Default: 0 11 | % crop_amounts - 4-element vector of crop amounts: [top,right,bottom,left] 12 | % where NaN/Inf indicate auto-cropping, 0 means no cropping, 13 | % and any other value mean cropping in pixel amounts. 14 | % 15 | %OUT: 16 | % B - JxKxCxN cropped stack of images. 17 | % vA - coordinates in A that contain the cropped image 18 | % vB - coordinates in B where the cropped version of A is placed 19 | % bb_rel - relative bounding box (used for eps-cropping) 20 | 21 | %{ 22 | % 06/03/15: Improved image cropping thanks to Oscar Hartogensis 23 | % 08/06/15: Fixed issue #76: case of transparent figure bgcolor 24 | % 21/02/16: Enabled specifying non-automated crop amounts 25 | % 04/04/16: Fix per Luiz Carvalho for old Matlab releases 26 | % 23/10/16: Fixed issue #175: there used to be a 1px minimal padding in case of crop, now removed 27 | %} 28 | 29 | if nargin < 3 30 | padding = 0; 31 | end 32 | if nargin < 4 33 | crop_amounts = nan(1,4); % =auto-cropping 34 | end 35 | crop_amounts(end+1:4) = NaN; % fill missing values with NaN 36 | 37 | [h, w, c, n] = size(A); 38 | if isempty(bcol) % case of transparent bgcolor 39 | bcol = A(ceil(end/2),1,:,1); 40 | end 41 | if isscalar(bcol) 42 | bcol = bcol(ones(c, 1)); 43 | end 44 | 45 | % Crop margin from left 46 | if ~isfinite(crop_amounts(4)) 47 | bail = false; 48 | for l = 1:w 49 | for a = 1:c 50 | if ~all(col(A(:,l,a,:)) == bcol(a)) 51 | bail = true; 52 | break; 53 | end 54 | end 55 | if bail 56 | break; 57 | end 58 | end 59 | else 60 | l = 1 + abs(crop_amounts(4)); 61 | end 62 | 63 | % Crop margin from right 64 | if ~isfinite(crop_amounts(2)) 65 | bcol = A(ceil(end/2),w,:,1); 66 | bail = false; 67 | for r = w:-1:l 68 | for a = 1:c 69 | if ~all(col(A(:,r,a,:)) == bcol(a)) 70 | bail = true; 71 | break; 72 | end 73 | end 74 | if bail 75 | break; 76 | end 77 | end 78 | else 79 | r = w - abs(crop_amounts(2)); 80 | end 81 | 82 | % Crop margin from top 83 | if ~isfinite(crop_amounts(1)) 84 | bcol = A(1,ceil(end/2),:,1); 85 | bail = false; 86 | for t = 1:h 87 | for a = 1:c 88 | if ~all(col(A(t,:,a,:)) == bcol(a)) 89 | bail = true; 90 | break; 91 | end 92 | end 93 | if bail 94 | break; 95 | end 96 | end 97 | else 98 | t = 1 + abs(crop_amounts(1)); 99 | end 100 | 101 | % Crop margin from bottom 102 | bcol = A(h,ceil(end/2),:,1); 103 | if ~isfinite(crop_amounts(3)) 104 | bail = false; 105 | for b = h:-1:t 106 | for a = 1:c 107 | if ~all(col(A(b,:,a,:)) == bcol(a)) 108 | bail = true; 109 | break; 110 | end 111 | end 112 | if bail 113 | break; 114 | end 115 | end 116 | else 117 | b = h - abs(crop_amounts(3)); 118 | end 119 | 120 | if padding == 0 % no padding 121 | % Issue #175: there used to be a 1px minimal padding in case of crop, now removed 122 | %{ 123 | if ~isequal([t b l r], [1 h 1 w]) % Check if we're actually croppping 124 | padding = 1; % Leave one boundary pixel to avoid bleeding on resize 125 | bcol(:) = nan; % make the 1px padding transparent 126 | end 127 | %} 128 | elseif abs(padding) < 1 % pad value is a relative fraction of image size 129 | padding = sign(padding)*round(mean([b-t r-l])*abs(padding)); % ADJUST PADDING 130 | else % pad value is in units of 1/72" points 131 | padding = round(padding); % fix cases of non-integer pad value 132 | end 133 | 134 | if padding > 0 % extra padding 135 | % Create an empty image, containing the background color, that has the 136 | % cropped image size plus the padded border 137 | B = repmat(bcol,[(b-t)+1+padding*2,(r-l)+1+padding*2,1,n]); % Fix per Luiz Carvalho 138 | % vA - coordinates in A that contain the cropped image 139 | vA = [t b l r]; 140 | % vB - coordinates in B where the cropped version of A will be placed 141 | vB = [padding+1, (b-t)+1+padding, padding+1, (r-l)+1+padding]; 142 | % Place the original image in the empty image 143 | B(vB(1):vB(2), vB(3):vB(4), :, :) = A(vA(1):vA(2), vA(3):vA(4), :, :); 144 | A = B; 145 | else % extra cropping 146 | vA = [t-padding b+padding l-padding r+padding]; 147 | A = A(vA(1):vA(2), vA(3):vA(4), :, :); 148 | vB = [NaN NaN NaN NaN]; 149 | end 150 | 151 | % For EPS cropping, determine the relative BoundingBox - bb_rel 152 | bb_rel = [l-1 h-b-1 r+1 h-t+1]./[w h w h]; 153 | end 154 | 155 | function A = col(A) 156 | A = A(:); 157 | end 158 | -------------------------------------------------------------------------------- /source_code/export_fig/eps2pdf.m: -------------------------------------------------------------------------------- 1 | function eps2pdf(source, dest, crop, append, gray, quality, gs_options) 2 | %EPS2PDF Convert an eps file to pdf format using ghostscript 3 | % 4 | % Examples: 5 | % eps2pdf source dest 6 | % eps2pdf(source, dest, crop) 7 | % eps2pdf(source, dest, crop, append) 8 | % eps2pdf(source, dest, crop, append, gray) 9 | % eps2pdf(source, dest, crop, append, gray, quality) 10 | % eps2pdf(source, dest, crop, append, gray, quality, gs_options) 11 | % 12 | % This function converts an eps file to pdf format. The output can be 13 | % optionally cropped and also converted to grayscale. If the output pdf 14 | % file already exists then the eps file can optionally be appended as a new 15 | % page on the end of the eps file. The level of bitmap compression can also 16 | % optionally be set. 17 | % 18 | % This function requires that you have ghostscript installed on your 19 | % system. Ghostscript can be downloaded from: http://www.ghostscript.com 20 | % 21 | % Inputs: 22 | % source - filename of the source eps file to convert. The filename is 23 | % assumed to already have the extension ".eps". 24 | % dest - filename of the destination pdf file. The filename is assumed 25 | % to already have the extension ".pdf". 26 | % crop - boolean indicating whether to crop the borders off the pdf. 27 | % Default: true. 28 | % append - boolean indicating whether the eps should be appended to the 29 | % end of the pdf as a new page (if the pdf exists already). 30 | % Default: false. 31 | % gray - boolean indicating whether the output pdf should be grayscale 32 | % or not. Default: false. 33 | % quality - scalar indicating the level of image bitmap quality to 34 | % output. A larger value gives a higher quality. quality > 100 35 | % gives lossless output. Default: ghostscript prepress default. 36 | % gs_options - optional ghostscript options (e.g.: '-dNoOutputFonts'). If 37 | % multiple options are needed, enclose in call array: {'-a','-b'} 38 | 39 | % Copyright (C) Oliver Woodford 2009-2014, Yair Altman 2015- 40 | 41 | % Suggestion of appending pdf files provided by Matt C at: 42 | % http://www.mathworks.com/matlabcentral/fileexchange/23629 43 | 44 | % Thank you Fabio Viola for pointing out compression artifacts, leading to the quality setting. 45 | % Thank you Scott for pointing out the subsampling of very small images, which was fixed for lossless compression settings. 46 | 47 | % 09/12/11: Pass font path to ghostscript 48 | % 26/02/15: If temp dir is not writable, use the dest folder for temp destination files (Javier Paredes) 49 | % 28/02/15: Enable users to specify optional ghostscript options (issue #36) 50 | % 01/03/15: Upon GS error, retry without the -sFONTPATH= option (this might solve 51 | % some /findfont errors according to James Rankin, FEX Comment 23/01/15) 52 | % 23/06/15: Added extra debug info in case of ghostscript error; code indentation 53 | % 04/10/15: Suggest a workaround for issue #41 (missing font path; thanks Mariia Fedotenkova) 54 | % 22/02/16: Bug fix from latest release of this file (workaround for issue #41) 55 | % 20/03/17: Added informational message in case of GS croak (issue #186) 56 | % 16/01/18: Improved appending of multiple EPS files into single PDF (issue #233; thanks @shartjen) 57 | % 18/10/19: Workaround for GS 9.51+ .setpdfwrite removal problem (issue #285) 58 | % 18/10/19: Warn when ignoring GS fontpath or quality options; clarified error messages 59 | % 15/01/20: Added information about the GS/destination filepath in case of error (issue #294) 60 | % 20/01/20: Attempted fix for issue #285: unsupported patch transparency in some Ghostscript versions 61 | % 12/02/20: Improved fix for issue #285: add -dNOSAFER and -dALLOWPSTRANSPARENCY (thanks @linasstonys) 62 | 63 | % Intialise the options string for ghostscript 64 | options = ['-q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile="' dest '"']; 65 | 66 | % Set crop option 67 | if nargin < 3 || crop 68 | options = [options ' -dEPSCrop']; 69 | end 70 | 71 | % Set the font path 72 | fp = font_path(); 73 | if ~isempty(fp) 74 | options = [options ' -sFONTPATH="' fp '"']; 75 | end 76 | 77 | % Set the grayscale option 78 | if nargin > 4 && gray 79 | options = [options ' -sColorConversionStrategy=Gray -dProcessColorModel=/DeviceGray']; 80 | end 81 | 82 | % Set the bitmap quality 83 | qualityOptions = ''; 84 | if nargin > 5 && ~isempty(quality) 85 | qualityOptions = ' -dAutoFilterColorImages=false -dAutoFilterGrayImages=false'; 86 | if quality > 100 87 | qualityOptions = [qualityOptions ' -dColorImageFilter=/FlateEncode -dGrayImageFilter=/FlateEncode']; 88 | qualityOptions = [qualityOptions ' -c ".setpdfwrite << /ColorImageDownsampleThreshold 10 /GrayImageDownsampleThreshold 10 >> setdistillerparams"']; 89 | else 90 | qualityOptions = [qualityOptions ' -dColorImageFilter=/DCTEncode -dGrayImageFilter=/DCTEncode']; 91 | v = 1 + (quality < 80); 92 | quality = 1 - quality / 100; 93 | s = sprintf('<< /QFactor %.2f /Blend 1 /HSample [%d 1 1 %d] /VSample [%d 1 1 %d] >>', quality, v, v, v, v); 94 | qualityOptions = [qualityOptions ' -c ".setpdfwrite << /ColorImageDict ' s ' /GrayImageDict ' s ' >> setdistillerparams"']; 95 | end 96 | options = [options qualityOptions]; 97 | end 98 | 99 | % Enable users to specify optional ghostscript options (issue #36) 100 | if nargin > 6 && ~isempty(gs_options) 101 | if iscell(gs_options) 102 | gs_options = sprintf(' %s',gs_options{:}); 103 | elseif ~ischar(gs_options) 104 | error('gs_options input argument must be a string or cell-array of strings'); 105 | else 106 | gs_options = [' ' gs_options]; 107 | end 108 | options = [options gs_options]; 109 | end 110 | 111 | % Check if the output file exists 112 | if nargin > 3 && append && exist(dest, 'file') == 2 113 | % File exists - append current figure to the end 114 | tmp_nam = [tempname '.pdf']; 115 | [fpath,fname,fext] = fileparts(tmp_nam); 116 | try 117 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 118 | fid = fopen(tmp_nam,'w'); 119 | fwrite(fid,1); 120 | fclose(fid); 121 | delete(tmp_nam); 122 | catch 123 | % Temp dir is not writable, so use the dest folder 124 | fpath = fileparts(dest); 125 | tmp_nam = fullfile(fpath,[fname fext]); 126 | end 127 | % Copy the existing (dest) pdf file to temporary folder 128 | copyfile(dest, tmp_nam); 129 | % Produce an interim pdf of the source eps, rather than adding the eps directly (issue #233) 130 | ghostscript([options ' -f "' source '"']); 131 | [~,fname] = fileparts(tempname); 132 | tmp_nam2 = fullfile(fpath,[fname fext]); % ensure using a writable folder (not necessarily tempdir) 133 | copyfile(dest, tmp_nam2); 134 | % Add the existing pdf and interim pdf as inputs to ghostscript 135 | %options = [options ' -f "' tmp_nam '" "' source '"']; % append the source eps to dest pdf 136 | options = [options ' -f "' tmp_nam '" "' tmp_nam2 '"']; % append the interim pdf to dest pdf 137 | try 138 | % Convert to pdf using ghostscript 139 | [status, message] = ghostscript(options); 140 | catch me 141 | % Delete the intermediate files and rethrow the error 142 | delete(tmp_nam); 143 | delete(tmp_nam2); 144 | rethrow(me); 145 | end 146 | % Delete the intermediate (temporary) files 147 | delete(tmp_nam); 148 | delete(tmp_nam2); 149 | else 150 | % File doesn't exist or should be over-written 151 | % Add the source eps file as input to ghostscript 152 | options = [options ' -f "' source '"']; 153 | % Convert to pdf using ghostscript 154 | [status, message] = ghostscript(options); 155 | end 156 | 157 | % Check for error 158 | if status 159 | % Catch and correct undefined .setopacityalpha errors (issue #285) 160 | % (see explanation inside print2eps.m) 161 | if ~isempty(regexpi(message,'undefined in .setopacityalpha')) 162 | % First try with -dNOSAFER and -dALLOWPSTRANSPARENCY (thanks @linasstonys) 163 | new_options = [options ' -dNOSAFER -dALLOWPSTRANSPARENCY']; 164 | [status, message] = ghostscript(new_options); 165 | if ~status % hurray! (no error) 166 | return 167 | elseif isempty(regexpi(message,'undefined in .setopacityalpha')) % still some other error 168 | options = new_options; 169 | else % we still get a .setopacityalpha error 170 | % Remove the transparency and retry 171 | fstrm = read_write_entire_textfile(source); 172 | fstrm = regexprep(fstrm, '0?\.\d+ .setopacityalpha \w+\n', ''); 173 | read_write_entire_textfile(source, fstrm); 174 | [status, message] = ghostscript(options); 175 | if ~status % hurray! (no error) 176 | % Alert the user that transparency is not supported 177 | warning('export_fig:GS:quality','Export_fig Face/Edge alpha transparancy is ignored - not supported by your Ghostscript version') 178 | return 179 | end 180 | end 181 | end 182 | 183 | % Retry without the -sFONTPATH= option (this might solve some GS 184 | % /findfont errors according to James Rankin, FEX Comment 23/01/15) 185 | orig_options = options; 186 | if ~isempty(fp) 187 | options = regexprep(options, ' -sFONTPATH=[^ ]+ ',' '); 188 | [status, message] = ghostscript(options); 189 | if ~status % hurray! (no error) 190 | warning('export_fig:GS:fontpath','Export_fig font option is ignored - not supported by your Ghostscript version') 191 | return 192 | end 193 | end 194 | 195 | % Retry without quality options (may solve problems with GS 9.51+, issue #285) 196 | if ~isempty(qualityOptions) 197 | options = strrep(orig_options, qualityOptions, ''); 198 | [status, message] = ghostscript(options); 199 | if ~status % hurray! (no error) 200 | warning('export_fig:GS:quality','Export_fig quality option is ignored - not supported by your Ghostscript version') 201 | return 202 | end 203 | end 204 | 205 | % Report error 206 | if isempty(message) 207 | error(['Unable to generate pdf. Ensure that the destination folder (' fileparts(dest) ') is writable.']); 208 | elseif ~isempty(strfind(message,'/typecheck in /findfont')) %#ok 209 | % Suggest a workaround for issue #41 (missing font path) 210 | font_name = strtrim(regexprep(message,'.*Operand stack:\s*(.*)\s*Execution.*','$1')); 211 | fprintf(2, 'Ghostscript error: could not find the following font(s): %s\n', font_name); 212 | %fpath = fileparts(mfilename('fullpath')); 213 | %gs_fonts_file = fullfile(fpath, '.ignore', 'gs_font_path.txt'); 214 | [unused, gs_fonts_file] = user_string('gs_font_path'); %#ok 215 | fprintf(2, ' try to add the font''s folder to your %s file\n\n', gs_fonts_file); 216 | error('export_fig error'); 217 | else 218 | gs_options = strtrim(gs_options); 219 | fprintf(2, '\nGhostscript error: '); 220 | msg = regexprep(message, '^Error: /([^\n]+).*', '$1'); 221 | if ~isempty(msg) && ~strcmp(msg,message) 222 | fprintf(2,'%s',msg); 223 | end 224 | fprintf(2, '\n * perhaps %s is open by another application\n', dest); 225 | if ~isempty(gs_options) 226 | fprintf(2, ' * or maybe your Ghostscript version does not accept the extra "%s" option(s) that you requested\n', gs_options); 227 | end 228 | fprintf(2, ' * or maybe you have another gs executable in your system''s path\n\n'); 229 | fprintf(2, 'Ghostscript path: %s\n', user_string('ghostscript')); 230 | fprintf(2, 'Ghostscript options: %s\n\n', orig_options); 231 | error(message); 232 | end 233 | end 234 | end 235 | 236 | % Function to return (and create, where necessary) the font path 237 | function fp = font_path() 238 | fp = user_string('gs_font_path'); 239 | if ~isempty(fp) 240 | return 241 | end 242 | % Create the path 243 | % Start with the default path 244 | fp = getenv('GS_FONTPATH'); 245 | % Add on the typical directories for a given OS 246 | if ispc 247 | if ~isempty(fp) 248 | fp = [fp ';']; 249 | end 250 | fp = [fp getenv('WINDIR') filesep 'Fonts']; 251 | else 252 | if ~isempty(fp) 253 | fp = [fp ':']; 254 | end 255 | fp = [fp '/usr/share/fonts:/usr/local/share/fonts:/usr/share/fonts/X11:/usr/local/share/fonts/X11:/usr/share/fonts/truetype:/usr/local/share/fonts/truetype']; 256 | end 257 | user_string('gs_font_path', fp); 258 | end 259 | -------------------------------------------------------------------------------- /source_code/export_fig/fix_lines.m: -------------------------------------------------------------------------------- 1 | %FIX_LINES Improves the line style of eps files generated by print 2 | % 3 | % Examples: 4 | % fix_lines fname 5 | % fix_lines fname fname2 6 | % fstrm_out = fixlines(fstrm_in) 7 | % 8 | % This function improves the style of lines in eps files generated by 9 | % MATLAB's print function, making them more similar to those seen on 10 | % screen. Grid lines are also changed from a dashed style to a dotted 11 | % style, for greater differentiation from dashed lines. 12 | % 13 | % The function also places embedded fonts after the postscript header, in 14 | % versions of MATLAB which place the fonts first (R2006b and earlier), in 15 | % order to allow programs such as Ghostscript to find the bounding box 16 | % information. 17 | % 18 | %IN: 19 | % fname - Name or path of source eps file. 20 | % fname2 - Name or path of destination eps file. Default: same as fname. 21 | % fstrm_in - File contents of a MATLAB-generated eps file. 22 | % 23 | %OUT: 24 | % fstrm_out - Contents of the eps file with line styles fixed. 25 | 26 | % Copyright: (C) Oliver Woodford, 2008-2014 27 | 28 | % The idea of editing the EPS file to change line styles comes from Jiro 29 | % Doke's FIXPSLINESTYLE (fex id: 17928) 30 | % The idea of changing dash length with line width came from comments on 31 | % fex id: 5743, but the implementation is mine :) 32 | 33 | % Thank you to Sylvain Favrot for bringing the embedded font/bounding box 34 | % interaction in older versions of MATLAB to my attention. 35 | % Thank you to D Ko for bringing an error with eps files with tiff previews 36 | % to my attention. 37 | % Thank you to Laurence K for suggesting the check to see if the file was 38 | % opened. 39 | 40 | % 01/03/15: Issue #20: warn users if using this function in HG2 (R2014b+) 41 | % 27/03/15: Fixed out of memory issue with enormous EPS files (generated by print() with OpenGL renderer), related to issue #39 42 | 43 | function fstrm = fix_lines(fstrm, fname2) 44 | 45 | % Issue #20: warn users if using this function in HG2 (R2014b+) 46 | if using_hg2 47 | warning('export_fig:hg2','The fix_lines function should not be used in this Matlab version.'); 48 | end 49 | 50 | if nargout == 0 || nargin > 1 51 | if nargin < 2 52 | % Overwrite the input file 53 | fname2 = fstrm; 54 | end 55 | % Read in the file 56 | fstrm = read_write_entire_textfile(fstrm); 57 | end 58 | 59 | % Move any embedded fonts after the postscript header 60 | if strcmp(fstrm(1:15), '%!PS-AdobeFont-') 61 | % Find the start and end of the header 62 | ind = regexp(fstrm, '[\n\r]%!PS-Adobe-'); 63 | [ind2, ind2] = regexp(fstrm, '[\n\r]%%EndComments[\n\r]+'); 64 | % Put the header first 65 | if ~isempty(ind) && ~isempty(ind2) && ind(1) < ind2(1) 66 | fstrm = fstrm([ind(1)+1:ind2(1) 1:ind(1) ind2(1)+1:end]); 67 | end 68 | end 69 | 70 | % Make sure all line width commands come before the line style definitions, 71 | % so that dash lengths can be based on the correct widths 72 | % Find all line style sections 73 | ind = [regexp(fstrm, '[\n\r]SO[\n\r]'),... % This needs to be here even though it doesn't have dots/dashes! 74 | regexp(fstrm, '[\n\r]DO[\n\r]'),... 75 | regexp(fstrm, '[\n\r]DA[\n\r]'),... 76 | regexp(fstrm, '[\n\r]DD[\n\r]')]; 77 | ind = sort(ind); 78 | % Find line width commands 79 | [ind2, ind3] = regexp(fstrm, '[\n\r]\d* w[\n\r]'); 80 | % Go through each line style section and swap with any line width commands 81 | % near by 82 | b = 1; 83 | m = numel(ind); 84 | n = numel(ind2); 85 | for a = 1:m 86 | % Go forwards width commands until we pass the current line style 87 | while b <= n && ind2(b) < ind(a) 88 | b = b + 1; 89 | end 90 | if b > n 91 | % No more width commands 92 | break; 93 | end 94 | % Check we haven't gone past another line style (including SO!) 95 | if a < m && ind2(b) > ind(a+1) 96 | continue; 97 | end 98 | % Are the commands close enough to be confident we can swap them? 99 | if (ind2(b) - ind(a)) > 8 100 | continue; 101 | end 102 | % Move the line style command below the line width command 103 | fstrm(ind(a)+1:ind3(b)) = [fstrm(ind(a)+4:ind3(b)) fstrm(ind(a)+1:ind(a)+3)]; 104 | b = b + 1; 105 | end 106 | 107 | % Find any grid line definitions and change to GR format 108 | % Find the DO sections again as they may have moved 109 | ind = int32(regexp(fstrm, '[\n\r]DO[\n\r]')); 110 | if ~isempty(ind) 111 | % Find all occurrences of what are believed to be axes and grid lines 112 | ind2 = int32(regexp(fstrm, '[\n\r] *\d* *\d* *mt *\d* *\d* *L[\n\r]')); 113 | if ~isempty(ind2) 114 | % Now see which DO sections come just before axes and grid lines 115 | ind2 = repmat(ind2', [1 numel(ind)]) - repmat(ind, [numel(ind2) 1]); 116 | ind2 = any(ind2 > 0 & ind2 < 12); % 12 chars seems about right 117 | ind = ind(ind2); 118 | % Change any regions we believe to be grid lines to GR 119 | fstrm(ind+1) = 'G'; 120 | fstrm(ind+2) = 'R'; 121 | end 122 | end 123 | 124 | % Define the new styles, including the new GR format 125 | % Dot and dash lengths have two parts: a constant amount plus a line width 126 | % variable amount. The constant amount comes after dpi2point, and the 127 | % variable amount comes after currentlinewidth. If you want to change 128 | % dot/dash lengths for a one particular line style only, edit the numbers 129 | % in the /DO (dotted lines), /DA (dashed lines), /DD (dot dash lines) and 130 | % /GR (grid lines) lines for the style you want to change. 131 | new_style = {'/dom { dpi2point 1 currentlinewidth 0.08 mul add mul mul } bdef',... % Dot length macro based on line width 132 | '/dam { dpi2point 2 currentlinewidth 0.04 mul add mul mul } bdef',... % Dash length macro based on line width 133 | '/SO { [] 0 setdash 0 setlinecap } bdef',... % Solid lines 134 | '/DO { [1 dom 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dotted lines 135 | '/DA { [4 dam 1.5 dam] 0 setdash 0 setlinecap } bdef',... % Dashed lines 136 | '/DD { [1 dom 1.2 dom 4 dam 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dot dash lines 137 | '/GR { [0 dpi2point mul 4 dpi2point mul] 0 setdash 1 setlinecap } bdef'}; % Grid lines - dot spacing remains constant 138 | 139 | % Construct the output 140 | % This is the original (memory-intensive) code: 141 | %first_sec = strfind(fstrm, '% line types:'); % Isolate line style definition section 142 | %[second_sec, remaining] = strtok(fstrm(first_sec+1:end), '/'); 143 | %[remaining, remaining] = strtok(remaining, '%'); 144 | %fstrm = [fstrm(1:first_sec) second_sec sprintf('%s\r', new_style{:}) remaining]; 145 | fstrm = regexprep(fstrm,'(% line types:.+?)/.+?%',['$1',sprintf('%s\r',new_style{:}),'%']); 146 | 147 | % Write the output file 148 | if nargout == 0 || nargin > 1 149 | read_write_entire_textfile(fname2, fstrm); 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /source_code/export_fig/ghostscript.m: -------------------------------------------------------------------------------- 1 | function varargout = ghostscript(cmd) 2 | %GHOSTSCRIPT Calls a local GhostScript executable with the input command 3 | % 4 | % Example: 5 | % [status result] = ghostscript(cmd) 6 | % 7 | % Attempts to locate a ghostscript executable, finally asking the user to 8 | % specify the directory ghostcript was installed into. The resulting path 9 | % is stored for future reference. 10 | % 11 | % Once found, the executable is called with the input command string. 12 | % 13 | % This function requires a Ghostscript installation on your system. 14 | % You can download Ghostscript from http://ghostscript.com (Windows/Linux) 15 | % or http://pages.uoregon.edu/koch (MacOS). 16 | % 17 | % IN: 18 | % cmd - Command string to be passed into ghostscript. 19 | % 20 | % OUT: 21 | % status - 0 iff command ran without problem. 22 | % result - Output from ghostscript. 23 | 24 | % Copyright: Oliver Woodford, 2009-2015, Yair Altman 2015- 25 | %{ 26 | % Thanks to Jonas Dorn for the fix for the title of the uigetdir window on Mac OS. 27 | % Thanks to Nathan Childress for the fix to default location on 64-bit Windows systems. 28 | % 27/04/11 - Find 64-bit Ghostscript on Windows. Thanks to Paul Durack and 29 | % Shaun Kline for pointing out the issue 30 | % 04/05/11 - Thanks to David Chorlian for pointing out an alternative 31 | % location for gs on linux. 32 | % 12/12/12 - Add extra executable name on Windows. Thanks to Ratish 33 | % Punnoose for highlighting the issue. 34 | % 28/06/13 - Fix error using GS 9.07 in Linux. Many thanks to Jannick 35 | % Steinbring for proposing the fix. 36 | % 24/10/13 - Fix error using GS 9.07 in Linux. Many thanks to Johannes 37 | % for the fix. 38 | % 23/01/14 - Add full path to ghostscript.txt in warning. Thanks to Koen 39 | % Vermeer for raising the issue. 40 | % 27/02/15 - If Ghostscript croaks, display suggested workarounds 41 | % 30/03/15 - Improved performance by caching status of GS path check, if ok 42 | % 14/05/15 - Clarified warning message in case GS path could not be saved 43 | % 29/05/15 - Avoid cryptic error in case the ghostscipt path cannot be saved (issue #74) 44 | % 10/11/15 - Custom GS installation webpage for MacOS. Thanks to Andy Hueni via FEX 45 | % 15/01/20 - Various message cleanups/fixes in case of errors 46 | %} 47 | 48 | try 49 | % Call ghostscript 50 | [varargout{1:nargout}] = system([gs_command(gs_path()) cmd]); 51 | catch err 52 | % Display possible workarounds for Ghostscript croaks 53 | url1 = 'https://github.com/altmany/export_fig/issues/12#issuecomment-61467998'; % issue #12 54 | url2 = 'https://github.com/altmany/export_fig/issues/20#issuecomment-63826270'; % issue #20 55 | hg2_str = ''; if using_hg2, hg2_str = ' or Matlab R2014a'; end 56 | fprintf(2, 'Ghostscript error. Rolling back to GS 9.10%s may possibly solve this:\n * %s ', hg2_str, hyperlink(url1)); 57 | if using_hg2 58 | fprintf(2, '(GS 9.10)\n * %s (R2014a)', hyperlink(url2)); 59 | end 60 | fprintf('\n\n'); 61 | if ismac || isunix 62 | url3 = 'https://github.com/altmany/export_fig/issues/27'; % issue #27 63 | fprintf(2, 'Alternatively, this may possibly be due to a font path issue:\n * %s\n\n', hyperlink(url3)); 64 | % issue #20 65 | % TODO: in Unix/Mac, find a way to automatically determine whether to use "export" (bash) or "setenv" (csh/tcsh) 66 | if isdeployed 67 | url = [mfilename '.m']; 68 | else 69 | fpath = which(mfilename); 70 | if isempty(fpath), fpath = [mfilename('fullpath') '.m']; end 71 | url = ['' fpath '']; 72 | end 73 | fprintf(2, 'Alternatively, if you are using csh, modify shell_cmd from "export ..." to "setenv ..."\nat the bottom of %s\n\n', url); 74 | end 75 | rethrow(err); 76 | end 77 | end 78 | 79 | function path_ = gs_path 80 | % Return a valid path 81 | % Start with the currently set path 82 | path_ = user_string('ghostscript'); 83 | % Check the path works 84 | if check_gs_path(path_) 85 | return 86 | end 87 | % Check whether the binary is on the path 88 | if ispc 89 | bin = {'gswin32c.exe', 'gswin64c.exe', 'gs'}; 90 | else 91 | bin = {'gs'}; 92 | end 93 | for a = 1:numel(bin) 94 | path_ = bin{a}; 95 | if check_store_gs_path(path_) 96 | return 97 | end 98 | end 99 | % Search the obvious places 100 | if ispc 101 | default_location = 'C:\Program Files\gs\'; 102 | dir_list = dir(default_location); 103 | if isempty(dir_list) 104 | default_location = 'C:\Program Files (x86)\gs\'; % Possible location on 64-bit systems 105 | dir_list = dir(default_location); 106 | end 107 | executable = {'\bin\gswin32c.exe', '\bin\gswin64c.exe'}; 108 | ver_num = 0; 109 | % If there are multiple versions, use the newest 110 | for a = 1:numel(dir_list) 111 | ver_num2 = sscanf(dir_list(a).name, 'gs%g'); 112 | if ~isempty(ver_num2) && ver_num2 > ver_num 113 | for b = 1:numel(executable) 114 | path2 = [default_location dir_list(a).name executable{b}]; 115 | if exist(path2, 'file') == 2 116 | path_ = path2; 117 | ver_num = ver_num2; 118 | end 119 | end 120 | end 121 | end 122 | if check_store_gs_path(path_) 123 | return 124 | end 125 | else 126 | executable = {'/usr/bin/gs', '/usr/local/bin/gs'}; 127 | for a = 1:numel(executable) 128 | path_ = executable{a}; 129 | if check_store_gs_path(path_) 130 | return 131 | end 132 | end 133 | end 134 | % Ask the user to enter the path 135 | while true 136 | if strncmp(computer, 'MAC', 3) % Is a Mac 137 | % Give separate warning as the uigetdir dialogue box doesn't have a 138 | % title on MacOS 139 | uiwait(warndlg('Ghostscript installation not found - please locate the program.', 'Ghostscript')) 140 | base = uigetdir('/', 'Ghostcript program location'); 141 | else 142 | base = uigetdir('/', 'Ghostcript program not found - please locate it'); 143 | end 144 | if isequal(base, 0) 145 | % User hit cancel or closed window 146 | break; 147 | end 148 | base = [base filesep]; %#ok 149 | bin_dir = {'', ['bin' filesep], ['lib' filesep]}; 150 | for a = 1:numel(bin_dir) 151 | for b = 1:numel(bin) 152 | path_ = [base bin_dir{a} bin{b}]; 153 | if exist(path_, 'file') == 2 154 | if check_store_gs_path(path_) 155 | return 156 | end 157 | end 158 | end 159 | end 160 | end 161 | if ismac 162 | url = 'http://pages.uoregon.edu/koch'; 163 | else 164 | url = 'http://ghostscript.com'; 165 | end 166 | error('Ghostscript:NotFound', 'Ghostscript not found. Have you installed it from %s ?', hyperlink(url)); 167 | end 168 | 169 | function good = check_store_gs_path(path_) 170 | % Check the path is valid 171 | good = check_gs_path(path_); 172 | if ~good 173 | return 174 | end 175 | % Update the current default path to the path found 176 | if ~user_string('ghostscript', path_) 177 | %filename = fullfile(fileparts(which('user_string.m')), '.ignore', 'ghostscript.txt'); 178 | [unused, filename] = user_string('ghostscript'); %#ok 179 | warning('Ghostscript:path', 'Path to ghostscript installation could not be saved in %s (perhaps a permissions issue). You can manually create this file and set its contents to %s, to improve performance in future invocations (this warning is safe to ignore).', filename, path_); 180 | return 181 | end 182 | end 183 | 184 | function good = check_gs_path(path_) 185 | persistent isOk 186 | if isempty(path_) 187 | isOk = false; 188 | elseif ~isequal(isOk,true) 189 | % Check whether the path is valid 190 | [status, message] = system([gs_command(path_) '-h']); %#ok 191 | isOk = status == 0; 192 | end 193 | good = isOk; 194 | end 195 | 196 | function cmd = gs_command(path_) 197 | % Initialize any required system calls before calling ghostscript 198 | % TODO: in Unix/Mac, find a way to automatically determine whether to use "export" (bash) or "setenv" (csh/tcsh) 199 | shell_cmd = ''; 200 | if isunix 201 | shell_cmd = 'export LD_LIBRARY_PATH=""; '; % Avoids an error on Linux with GS 9.07 202 | end 203 | if ismac 204 | shell_cmd = 'export DYLD_LIBRARY_PATH=""; '; % Avoids an error on Mac with GS 9.07 205 | end 206 | % Construct the command string 207 | cmd = sprintf('%s"%s" ', shell_cmd, path_); 208 | end 209 | -------------------------------------------------------------------------------- /source_code/export_fig/hyperlink.m: -------------------------------------------------------------------------------- 1 | function str = hyperlink(url, label, msg) 2 | %HYPERLINK create a string that is displayable as hyperlink in Matlab console 3 | % 4 | % Usage examples: 5 | % fprintf('Search on %s\n', hyperlink('http://google.com','Google')); 6 | % fprintf(hyperlink('http://google.com','Google','Search on Google\n')); 7 | % 8 | % HYPERLINK converts the specified URL and text label into a string that is 9 | % displayed as a hyperlink in the Matlab console (Command Window). 10 | % In a deployed (compiled) program, the URL and text label (if different 11 | % from the URL) are displayed in a non-hyperlinked plain-text manner. 12 | % If the optional 3rd input argument (msg) is specified, then all instances of 13 | % the specified label within msg will be handled as above (hyperlinked etc.) 14 | % 15 | % IN: 16 | % url - (mandatory) URL of webpage or Matlab command (e.g., 'matlab:which(...') 17 | % label - (optional) text label of the hyperlink. Default: url 18 | % msg - (optional) string in which all label instances should be hyperlinked 19 | % 20 | % OUT: 21 | % str - string output 22 | 23 | % Copyright: Yair Altman 2020- 24 | %{ 25 | % 15/01/20 - Initial version 26 | %} 27 | 28 | error(nargchk(1,3,nargin)); %#ok narginchk is only available in R2011b+ 29 | if nargin > 2 % msg was specified 30 | % replace all instances of label within msg with corresponding hyperlink 31 | str = strrep(msg, label, hyperlink(url,label)); 32 | return 33 | end 34 | if nargin < 2, label = url; end 35 | isWebpage = strncmpi(url,'http',4); 36 | 37 | % Only hyperlink in interactive Matlab sessions, not in a deployed program 38 | if ~isdeployed % interactive Matlab session 39 | if isWebpage % open in a web browser 40 | str = ['' label '']; 41 | else % Matlab command e.g. 'matlab:which(...' 42 | str = ['' label '']; 43 | end 44 | else % deployed (compiled) 45 | if isWebpage && ~strcmp(label,url) % display label next to url 46 | str = [label ' (' url ')']; 47 | elseif isWebpage % text==url - display only the url 48 | str = url; 49 | else % Matlab command (not a webpage) - just display the label 50 | str = label; 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /source_code/export_fig/im2gif.m: -------------------------------------------------------------------------------- 1 | %IM2GIF Convert a multiframe image to an animated GIF file 2 | % 3 | % Examples: 4 | % im2gif infile 5 | % im2gif infile outfile 6 | % im2gif(A, outfile) 7 | % im2gif(..., '-nocrop') 8 | % im2gif(..., '-nodither') 9 | % im2gif(..., '-ncolors', n) 10 | % im2gif(..., '-loops', n) 11 | % im2gif(..., '-delay', n) 12 | % 13 | % This function converts a multiframe image to an animated GIF. 14 | % 15 | % To create an animation from a series of figures, export to a multiframe 16 | % TIFF file using export_fig, then convert to a GIF, as follows: 17 | % 18 | % for a = 2 .^ (3:6) 19 | % peaks(a); 20 | % export_fig test.tif -nocrop -append 21 | % end 22 | % im2gif('test.tif', '-delay', 0.5); 23 | % 24 | %IN: 25 | % infile - string containing the name of the input image. 26 | % outfile - string containing the name of the output image (must have the 27 | % .gif extension). Default: infile, with .gif extension. 28 | % A - HxWxCxN array of input images, stacked along fourth dimension, to 29 | % be converted to gif. 30 | % -nocrop - option indicating that the borders of the output are not to 31 | % be cropped. 32 | % -nodither - option indicating that dithering is not to be used when 33 | % converting the image. 34 | % -ncolors - option pair, the value of which indicates the maximum number 35 | % of colors the GIF can have. This can also be a quantization 36 | % tolerance, between 0 and 1. Default/maximum: 256. 37 | % -loops - option pair, the value of which gives the number of times the 38 | % animation is to be looped. Default: 65535. 39 | % -delay - option pair, the value of which gives the time, in seconds, 40 | % between frames. Default: 1/15. 41 | 42 | % Copyright (C) Oliver Woodford 2011 43 | 44 | %{ 45 | % 14/02/18: Merged issue #235: reduced memory usage, improved performance (thanks to @numb7rs) 46 | % 30/11/19: Merged issue #288: Fix im2gif.m for greyscale TIFF images (thanks @Blackbelt1221) 47 | %} 48 | 49 | function im2gif(A, varargin) 50 | 51 | % Parse the input arguments 52 | [A, options] = parse_args(A, varargin{:}); 53 | 54 | if options.crop ~= 0 55 | % Crop 56 | A = crop_borders(A, A(ceil(end/2),1,:,1)); 57 | end 58 | 59 | % Convert to indexed image 60 | [h, w, c, n] = size(A); 61 | 62 | % Issue #235: Using unique(A,'rows') on the whole image stack at once causes 63 | % massive memory usage when dealing with large images (at least on Matlab 2017b). 64 | % Running unique(...) on individual frames, then again on the results drastically 65 | % reduces the memory usage & slightly improves the execution time (@numb7rs). 66 | uns = cell(1,size(A,4)); 67 | for nn=1:size(A,4) 68 | uns{nn}=unique(reshape(A(:,:,:,nn), h*w, c),'rows'); 69 | end 70 | map=unique(cell2mat(uns'),'rows'); 71 | 72 | A = reshape(permute(A, [1 2 4 3]), h, w*n, c); 73 | 74 | if size(map, 1) > 256 75 | dither_str = {'dither', 'nodither'}; 76 | dither_str = dither_str{1+(options.dither==0)}; 77 | if options.ncolors <= 1 78 | [B, map] = rgb2ind(A, options.ncolors, dither_str); 79 | if size(map, 1) > 256 80 | [B, map] = rgb2ind(A, 256, dither_str); 81 | end 82 | else 83 | [B, map] = rgb2ind(A, min(round(options.ncolors), 256), dither_str); 84 | end 85 | else 86 | if max(map(:)) > 1 87 | map = double(map) / 255; 88 | A = double(A) / 255; 89 | end 90 | B = rgb2ind(im2double(A), map); 91 | end 92 | B = reshape(B, h, w, 1, n); 93 | 94 | % Bug fix to rgb2ind 95 | map(B(1)+1,:) = im2double(A(1,1,:)); 96 | 97 | % Save as a gif 98 | imwrite(B, map, options.outfile, 'LoopCount', round(options.loops(1)), 'DelayTime', options.delay); 99 | end 100 | 101 | %% Parse the input arguments 102 | function [A, options] = parse_args(A, varargin) 103 | % Set the defaults 104 | options = struct('outfile', '', ... 105 | 'dither', true, ... 106 | 'crop', true, ... 107 | 'ncolors', 256, ... 108 | 'loops', 65535, ... 109 | 'delay', 1/15); 110 | 111 | % Go through the arguments 112 | a = 0; 113 | n = numel(varargin); 114 | while a < n 115 | a = a + 1; 116 | if ischar(varargin{a}) && ~isempty(varargin{a}) 117 | if varargin{a}(1) == '-' 118 | opt = lower(varargin{a}(2:end)); 119 | switch opt 120 | case 'nocrop' 121 | options.crop = false; 122 | case 'nodither' 123 | options.dither = false; 124 | otherwise 125 | if ~isfield(options, opt) 126 | error('Option %s not recognized', varargin{a}); 127 | end 128 | a = a + 1; 129 | if ischar(varargin{a}) && ~ischar(options.(opt)) 130 | options.(opt) = str2double(varargin{a}); 131 | else 132 | options.(opt) = varargin{a}; 133 | end 134 | end 135 | else 136 | options.outfile = varargin{a}; 137 | end 138 | end 139 | end 140 | 141 | if isempty(options.outfile) 142 | if ~ischar(A) 143 | error('No output filename given.'); 144 | end 145 | % Generate the output filename from the input filename 146 | [path, outfile] = fileparts(A); 147 | options.outfile = fullfile(path, [outfile '.gif']); 148 | end 149 | 150 | if ischar(A) 151 | % Read in the image 152 | A = imread_rgb(A); 153 | end 154 | end 155 | 156 | %% Read image to uint8 rgb array 157 | function [A, alpha] = imread_rgb(name) 158 | % Get file info 159 | info = imfinfo(name); 160 | % Special case formats 161 | switch lower(info(1).Format) 162 | case 'gif' 163 | [A, map] = imread(name, 'frames', 'all'); 164 | if ~isempty(map) 165 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 166 | A = reshape(map(uint32(A)+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 167 | A = permute(A, [1 2 5 4 3]); 168 | end 169 | case {'tif', 'tiff'} 170 | A = cell(numel(info), 1); 171 | for a = 1:numel(A) 172 | [A{a}, map] = imread(name, 'Index', a, 'Info', info); 173 | if ~isempty(map) 174 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 175 | A{a} = reshape(map(uint32(A{a})+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 176 | end 177 | if size(A{a}, 3) == 4 178 | % TIFF in CMYK colourspace - convert to RGB 179 | if isfloat(A{a}) 180 | A{a} = A{a} * 255; 181 | else 182 | A{a} = single(A{a}); 183 | end 184 | A{a} = 255 - A{a}; 185 | A{a}(:,:,4) = A{a}(:,:,4) / 255; 186 | A{a} = uint8(A(:,:,1:3) .* A{a}(:,:,[4 4 4])); 187 | elseif size(A{a}, 3) < 3 %Check whether TIFF has been read in as greyscale 188 | %Convert from greyscale to RGB colorspace (issue #288) 189 | A{a} = cat(3, A{a}, A{a}, A{a}); 190 | end 191 | end 192 | A = cat(4, A{:}); 193 | otherwise 194 | [A, map, alpha] = imread(name); 195 | A = A(:,:,:,1); % Keep only first frame of multi-frame files 196 | if ~isempty(map) 197 | map = uint8(map * 256 - 0.5); % Convert to uint8 for storage 198 | A = reshape(map(uint32(A)+1,:), [size(A) size(map, 2)]); % Assume indexed from 0 199 | elseif size(A, 3) == 4 200 | % Assume 4th channel is an alpha matte 201 | alpha = A(:,:,4); 202 | A = A(:,:,1:3); 203 | end 204 | end 205 | end 206 | -------------------------------------------------------------------------------- /source_code/export_fig/isolate_axes.m: -------------------------------------------------------------------------------- 1 | function fh = isolate_axes(ah, vis) 2 | %ISOLATE_AXES Isolate the specified axes in a figure on their own 3 | % 4 | % Examples: 5 | % fh = isolate_axes(ah) 6 | % fh = isolate_axes(ah, vis) 7 | % 8 | % This function will create a new figure containing the axes/uipanels 9 | % specified, and also their associated legends and colorbars. The objects 10 | % specified must all be in the same figure, but they will generally only be 11 | % a subset of the objects in the figure. 12 | % 13 | % IN: 14 | % ah - An array of axes and uipanel handles, which must come from the 15 | % same figure. 16 | % vis - A boolean indicating whether the new figure should be visible. 17 | % Default: false. 18 | % 19 | % OUT: 20 | % fh - The handle of the created figure. 21 | 22 | % Copyright (C) Oliver Woodford 2011-2013 23 | 24 | % Thank you to Rosella Blatt for reporting a bug to do with axes in GUIs 25 | % 16/03/12: Moved copyfig to its own function. Thanks to Bob Fratantonio 26 | % for pointing out that the function is also used in export_fig.m 27 | % 12/12/12: Add support for isolating uipanels. Thanks to michael for suggesting it 28 | % 08/10/13: Bug fix to allchildren suggested by Will Grant (many thanks!) 29 | % 05/12/13: Bug fix to axes having different units. Thanks to Remington Reid for reporting 30 | % 21/04/15: Bug fix for exporting uipanels with legend/colorbar on HG1 (reported by Alvaro 31 | % on FEX page as a comment on 24-Apr-2014); standardized indentation & help section 32 | % 22/04/15: Bug fix: legends and colorbars were not exported when exporting axes handle in HG2 33 | 34 | % Make sure we have an array of handles 35 | if ~all(ishandle(ah)) 36 | error('ah must be an array of handles'); 37 | end 38 | % Check that the handles are all for axes or uipanels, and are all in the same figure 39 | fh = ancestor(ah(1), 'figure'); 40 | nAx = numel(ah); 41 | for a = 1:nAx 42 | if ~ismember(get(ah(a), 'Type'), {'axes', 'uipanel'}) 43 | error('All handles must be axes or uipanel handles.'); 44 | end 45 | if ~isequal(ancestor(ah(a), 'figure'), fh) 46 | error('Axes must all come from the same figure.'); 47 | end 48 | end 49 | % Tag the objects so we can find them in the copy 50 | old_tag = get(ah, 'Tag'); 51 | if nAx == 1 52 | old_tag = {old_tag}; 53 | end 54 | set(ah, 'Tag', 'ObjectToCopy'); 55 | % Create a new figure exactly the same as the old one 56 | fh = copyfig(fh); %copyobj(fh, 0); 57 | if nargin < 2 || ~vis 58 | set(fh, 'Visible', 'off'); 59 | end 60 | % Reset the object tags 61 | for a = 1:nAx 62 | set(ah(a), 'Tag', old_tag{a}); 63 | end 64 | % Find the objects to save 65 | ah = findall(fh, 'Tag', 'ObjectToCopy'); 66 | if numel(ah) ~= nAx 67 | close(fh); 68 | error('Incorrect number of objects found.'); 69 | end 70 | % Set the axes tags to what they should be 71 | for a = 1:nAx 72 | set(ah(a), 'Tag', old_tag{a}); 73 | end 74 | % Keep any legends and colorbars which overlap the subplots 75 | % Note: in HG1 these are axes objects; in HG2 they are separate objects, therefore we 76 | % don't test for the type, only the tag (hopefully nobody but Matlab uses them!) 77 | lh = findall(fh, 'Tag', 'legend', '-or', 'Tag', 'Colorbar'); 78 | nLeg = numel(lh); 79 | if nLeg > 0 80 | set([ah(:); lh(:)], 'Units', 'normalized'); 81 | try 82 | ax_pos = get(ah, 'OuterPosition'); % axes and figures have the OuterPosition property 83 | catch 84 | ax_pos = get(ah, 'Position'); % uipanels only have Position, not OuterPosition 85 | end 86 | if nAx > 1 87 | ax_pos = cell2mat(ax_pos(:)); 88 | end 89 | ax_pos(:,3:4) = ax_pos(:,3:4) + ax_pos(:,1:2); 90 | try 91 | leg_pos = get(lh, 'OuterPosition'); 92 | catch 93 | leg_pos = get(lh, 'Position'); % No OuterPosition in HG2, only in HG1 94 | end 95 | if nLeg > 1; 96 | leg_pos = cell2mat(leg_pos); 97 | end 98 | leg_pos(:,3:4) = leg_pos(:,3:4) + leg_pos(:,1:2); 99 | ax_pos = shiftdim(ax_pos, -1); 100 | % Overlap test 101 | M = bsxfun(@lt, leg_pos(:,1), ax_pos(:,:,3)) & ... 102 | bsxfun(@lt, leg_pos(:,2), ax_pos(:,:,4)) & ... 103 | bsxfun(@gt, leg_pos(:,3), ax_pos(:,:,1)) & ... 104 | bsxfun(@gt, leg_pos(:,4), ax_pos(:,:,2)); 105 | ah = [ah; lh(any(M, 2))]; 106 | end 107 | % Get all the objects in the figure 108 | axs = findall(fh); 109 | % Delete everything except for the input objects and associated items 110 | delete(axs(~ismember(axs, [ah; allchildren(ah); allancestors(ah)]))); 111 | end 112 | 113 | function ah = allchildren(ah) 114 | ah = findall(ah); 115 | if iscell(ah) 116 | ah = cell2mat(ah); 117 | end 118 | ah = ah(:); 119 | end 120 | 121 | function ph = allancestors(ah) 122 | ph = []; 123 | for a = 1:numel(ah) 124 | h = get(ah(a), 'parent'); 125 | while h ~= 0 126 | ph = [ph; h]; 127 | h = get(h, 'parent'); 128 | end 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /source_code/export_fig/pdf2eps.m: -------------------------------------------------------------------------------- 1 | %PDF2EPS Convert a pdf file to eps format using pdftops 2 | % 3 | % Examples: 4 | % pdf2eps source dest 5 | % 6 | % This function converts a pdf file to eps format. 7 | % 8 | % This function requires that you have pdftops, from the Xpdf suite of 9 | % functions, installed on your system. This can be downloaded from: 10 | % http://xpdfreader.com 11 | % 12 | % Inputs: 13 | % source - filename of the source pdf file to convert. The filename is 14 | % assumed to already have the extension ".pdf". 15 | % dest - filename of the destination eps file. The filename is assumed to 16 | % already have the extension ".eps". 17 | 18 | % Copyright (C) Oliver Woodford 2009-2010, Yair Altman 2015- 19 | 20 | % Thanks to Aldebaro Klautau for reporting a bug when saving to 21 | % non-existant directories. 22 | 23 | % 22/09/2018 - Xpdf website changed to xpdfreader.com 24 | 25 | function pdf2eps(source, dest) 26 | % Construct the options string for pdftops 27 | options = ['-q -paper match -eps -level2 "' source '" "' dest '"']; 28 | 29 | % Convert to eps using pdftops 30 | [status, message] = pdftops(options); 31 | 32 | % Check for error 33 | if status 34 | % Report error 35 | if isempty(message) 36 | error('Unable to generate eps. Check destination directory is writable.'); 37 | else 38 | error(message); 39 | end 40 | end 41 | 42 | % Fix the DSC error created by pdftops 43 | fid = fopen(dest, 'r+'); 44 | if fid == -1 45 | % Cannot open the file 46 | return 47 | end 48 | fgetl(fid); % Get the first line 49 | str = fgetl(fid); % Get the second line 50 | if strcmp(str(1:min(13, end)), '% Produced by') 51 | fseek(fid, -numel(str)-1, 'cof'); 52 | fwrite(fid, '%'); % Turn ' ' into '%' 53 | end 54 | fclose(fid); 55 | end 56 | -------------------------------------------------------------------------------- /source_code/export_fig/pdftops.m: -------------------------------------------------------------------------------- 1 | function varargout = pdftops(cmd) 2 | %PDFTOPS Calls a local pdftops executable with the input command 3 | % 4 | % Example: 5 | % [status result] = pdftops(cmd) 6 | % 7 | % Attempts to locate a pdftops executable, finally asking the user to 8 | % specify the directory pdftops was installed into. The resulting path is 9 | % stored for future reference. 10 | % 11 | % Once found, the executable is called with the input command string. 12 | % 13 | % This function requires that you have pdftops (from the Xpdf package) 14 | % installed on your system. You can download this from: http://xpdfreader.com 15 | % 16 | % IN: 17 | % cmd - Command string to be passed into pdftops (e.g. '-help'). 18 | % 19 | % OUT: 20 | % status - 0 iff command ran without problem. 21 | % result - Output from pdftops. 22 | 23 | % Copyright: Oliver Woodford, 2009-2010 24 | 25 | % Thanks to Jonas Dorn for the fix for the title of the uigetdir window on Mac OS. 26 | % Thanks to Christoph Hertel for pointing out a bug in check_xpdf_path under linux. 27 | % 23/01/2014 - Add full path to pdftops.txt in warning. 28 | % 27/05/2015 - Fixed alert in case of missing pdftops; fixed code indentation 29 | % 02/05/2016 - Added possible error explanation suggested by Michael Pacer (issue #137) 30 | % 02/05/2016 - Search additional possible paths suggested by Jonas Stein (issue #147) 31 | % 03/05/2016 - Display the specific error message if pdftops fails for some reason (issue #148) 32 | % 22/09/2018 - Xpdf website changed to xpdfreader.com; improved popup logic 33 | % 03/02/2019 - Fixed one-off 'pdftops not found' error after install (Mac/Linux) (issue #266) 34 | % 15/01/2020 - Fixed reported path of pdftops.txt file in case of error; added warning ID 35 | % 23/07/2020 - Fixed issue #311 (confusion regarding Xpdf-tools download/installation); silent check of pdftops installation in case no input arg specified 36 | 37 | % If no command parameter specified, just check pdftops installation and bail out 38 | if nargin < 1 39 | xpdf_path(); % this will error if pdftops is not found 40 | return % silent bail-out if pdftops was successfully located 41 | end 42 | 43 | % Call pdftops 44 | [varargout{1:nargout}] = system([xpdf_command(xpdf_path()) cmd]); 45 | end 46 | 47 | function path_ = xpdf_path 48 | % Return a valid path 49 | % Start with the currently set path 50 | path_ = user_string('pdftops'); 51 | % Check the path works 52 | if check_xpdf_path(path_) 53 | return 54 | end 55 | % Check whether the binary is on the path 56 | if ispc 57 | bin = 'pdftops.exe'; 58 | else 59 | bin = 'pdftops'; 60 | end 61 | if check_store_xpdf_path(bin) 62 | path_ = bin; 63 | return 64 | end 65 | % Search the obvious places 66 | if ispc 67 | paths = {'C:\Program Files\xpdf\pdftops.exe', 'C:\Program Files (x86)\xpdf\pdftops.exe'}; 68 | else 69 | paths = {'/usr/bin/pdftops', '/usr/local/bin/pdftops'}; 70 | end 71 | for a = 1:numel(paths) 72 | path_ = paths{a}; 73 | if check_store_xpdf_path(path_) 74 | return 75 | end 76 | end 77 | 78 | % Ask the user to enter the path 79 | errMsg1 = 'Pdftops utility not found. Please locate the program, or install xpdf-tools from '; 80 | url1 = 'http://xpdfreader.com/download.html'; %='http://foolabs.com/xpdf'; 81 | fprintf(2, '%s%s ("Xpdf command line tools" section)\n', errMsg1, hyperlink(url1)); 82 | errMsg1 = [errMsg1 url1]; 83 | %if strncmp(computer,'MAC',3) % Is a Mac 84 | % % Give separate warning as the MacOS uigetdir dialogue box doesn't have a title 85 | % uiwait(warndlg(errMsg1)) 86 | %end 87 | 88 | % Provide an alternative possible explanation as per issue #137 89 | errMsg2 = 'If pdftops is installed, maybe Matlab is shaddowing it, as described in '; 90 | url2 = 'https://github.com/altmany/export_fig/issues/137'; 91 | fprintf(2, '%s%s\n', errMsg2, hyperlink(url2,'issue #137')); 92 | errMsg2 = [errMsg2 url2]; 93 | 94 | % Provide an alternative possible explanation as per issue #311 95 | errMsg3 = 'Or perhaps you installed XpdfReader but not xpdf-tools, as described in '; 96 | url3 = 'https://github.com/altmany/export_fig/issues/311'; 97 | fprintf(2, '%s%s\n', errMsg3, hyperlink(url3,'issue #311')); 98 | errMsg3 = [errMsg3 url3]; 99 | 100 | state = 1; 101 | while 1 102 | if state 103 | option1 = 'Install pdftops'; 104 | else 105 | option1 = 'Issue #137'; 106 | end 107 | answer = questdlg({errMsg1,'',errMsg2,'',errMsg3},'Pdftops error',option1,'Locate pdftops','Cancel','Cancel'); 108 | drawnow; % prevent a Matlab hang: http://undocumentedmatlab.com/blog/solving-a-matlab-hang-problem 109 | switch answer 110 | case 'Install pdftops' 111 | web('-browser',url1); 112 | state = 0; 113 | case 'Issue #137' 114 | web('-browser',url2); 115 | state = 1; 116 | case 'Locate pdftops' 117 | base = uigetdir('/', errMsg1); 118 | if isequal(base, 0) 119 | % User hit cancel or closed window 120 | break 121 | end 122 | base = [base filesep]; %#ok 123 | bin_dir = {'', ['bin' filesep], ['lib' filesep]}; 124 | for a = 1:numel(bin_dir) 125 | path_ = [base bin_dir{a} bin]; 126 | if exist(path_, 'file') == 2 127 | break 128 | end 129 | end 130 | if check_store_xpdf_path(path_) 131 | return 132 | end 133 | 134 | otherwise % User hit Cancel or closed window 135 | break 136 | end 137 | end 138 | error('pdftops executable not found.'); 139 | end 140 | 141 | function good = check_store_xpdf_path(path_) 142 | % Check the path is valid 143 | good = check_xpdf_path(path_); 144 | if ~good 145 | return 146 | end 147 | % Update the current default path to the path found 148 | if ~user_string('pdftops', path_) 149 | %filename = fullfile(fileparts(which('user_string.m')), '.ignore', 'pdftops.txt'); 150 | [unused, filename] = user_string('pdftops'); %#ok 151 | warning('export_fig:pdftops','Path to pdftops executable could not be saved. Enter it manually in %s.', filename); 152 | return 153 | end 154 | end 155 | 156 | function good = check_xpdf_path(path_) 157 | % Check the path is valid 158 | [good, message] = system([xpdf_command(path_) '-h']); %#ok 159 | % system returns good = 1 even when the command runs 160 | % Look for something distinct in the help text 161 | good = ~isempty(strfind(message, 'PostScript')); %#ok 162 | 163 | % Display the error message if the pdftops executable exists but fails for some reason 164 | % Note: on Mac/Linux, exist('pdftops','file') will always return 2 due to pdftops.m => check for '/','.' (issue #266) 165 | if ~good && exist(path_,'file') && ~isempty(regexp(path_,'[/.]')) %#ok % file exists but generates an error 166 | fprintf('Error running %s:\n', path_); 167 | fprintf(2,'%s\n\n',message); 168 | end 169 | end 170 | 171 | function cmd = xpdf_command(path_) 172 | % Initialize any required system calls before calling ghostscript 173 | % TODO: in Unix/Mac, find a way to determine whether to use "export" (bash) or "setenv" (csh/tcsh) 174 | shell_cmd = ''; 175 | if isunix 176 | % Avoids an error on Linux with outdated MATLAB lib files 177 | % R20XXa/bin/glnxa64/libtiff.so.X 178 | % R20XXa/sys/os/glnxa64/libstdc++.so.X 179 | shell_cmd = 'export LD_LIBRARY_PATH=""; '; 180 | end 181 | if ismac 182 | shell_cmd = 'export DYLD_LIBRARY_PATH=""; '; 183 | end 184 | % Construct the command string 185 | cmd = sprintf('%s"%s" ', shell_cmd, path_); 186 | end 187 | -------------------------------------------------------------------------------- /source_code/export_fig/print2array.m: -------------------------------------------------------------------------------- 1 | function [A, bcol, alpha] = print2array(fig, res, renderer, gs_options) 2 | %PRINT2ARRAY Exports a figure to a bitmap RGB image array 3 | % 4 | % Examples: 5 | % A = print2array 6 | % A = print2array(figure_handle) 7 | % A = print2array(figure_handle, resolution) 8 | % A = print2array(figure_handle, resolution, renderer) 9 | % A = print2array(figure_handle, resolution, renderer, gs_options) 10 | % [A, bcol, alpha] = print2array(...) 11 | % 12 | % This function outputs a bitmap image of a figure, at the desired resolution. 13 | % 14 | % When resolution==1, fast Java screen-capture is attempted first. 15 | % If the Java screen-capture fails or if resolution~=1, the builtin print() 16 | % function is used to create a temp TIF file, which is then loaded and reported. 17 | % If this fails, print() is used to create a temp EPS file which is converted to 18 | % a TIF file using Ghostcript (http://www.ghostscript.com), loaded and reported. 19 | % 20 | % Inputs: 21 | % figure_handle - The handle of the figure to be exported. Default: gcf. 22 | % resolution - Output resolution as a factor of screen resolution. Default: 1 23 | % Note: resolution ~= 1 uses a slow print to/from image file 24 | % renderer - The renderer to be used by print() function. Default: '-opengl' 25 | % Note: only used when resolution ~= 1 26 | % gs_options - optional ghostscript parameters (e.g.: '-dNoOutputFonts'). 27 | % Enclose multiple options in a cell array, e.g. {'-a','-b'} 28 | % Note: only used when resolution ~= 1 and basic print() fails 29 | % 30 | % Outputs: 31 | % A - MxNx3 uint8 bitmap image of the figure (MxN pixels x 3 RGB values) 32 | % bcol - 1x3 uint8 vector of the background RGB color 33 | % alpha - MxN uint8 array of alpha values (between 0=transparent, 255=opaque) 34 | 35 | % Copyright (C) Oliver Woodford 2008-2014, Yair Altman 2015- 36 | %{ 37 | % 05/09/11: Set EraseModes to normal when using opengl or zbuffer 38 | % renderers. Thanks to Pawel Kocieniewski for reporting the issue. 39 | % 21/09/11: Bug fix: unit8 -> uint8! Thanks to Tobias Lamour for reporting it. 40 | % 14/11/11: Bug fix: stop using hardcopy(), as it interfered with figure size 41 | % and erasemode settings. Makes it a bit slower, but more reliable. 42 | % Thanks to Phil Trinh and Meelis Lootus for reporting the issues. 43 | % 09/12/11: Pass font path to ghostscript. 44 | % 27/01/12: Bug fix affecting painters rendering tall figures. Thanks to 45 | % Ken Campbell for reporting it. 46 | % 03/04/12: Bug fix to median input. Thanks to Andy Matthews for reporting it. 47 | % 26/10/12: Set PaperOrientation to portrait. Thanks to Michael Watts for 48 | % reporting the issue. 49 | % 26/02/15: If temp dir is not writable, use the current folder for temp 50 | % EPS/TIF files (Javier Paredes) 51 | % 27/02/15: Display suggested workarounds to internal print() error (issue #16) 52 | % 28/02/15: Enable users to specify optional ghostscript options (issue #36) 53 | % 10/03/15: Fixed minor warning reported by Paul Soderlind; fixed code indentation 54 | % 28/05/15: Fixed issue #69: patches with LineWidth==0.75 appear wide (internal bug in Matlab's print() func) 55 | % 07/07/15: Fixed issue #83: use numeric handles in HG1 56 | % 11/12/16: Fixed cropping issue reported by Harry D. 57 | % 29/09/18: Fixed issue #254: error in print2array>read_tif_img 58 | % 22/03/20: Alert if ghostscript.m is required but not found on Matlab path 59 | % 24/05/20: Significant performance speedup; added alpha values (where possible) 60 | % 07/07/20: Fixed issue #308: bug in R2019a and earlier 61 | % 07/10/20: Use JavaFrame_I where possible, to avoid evoking a JavaFrame warning 62 | %} 63 | 64 | % Generate default input arguments, if needed 65 | if nargin < 1, fig = gcf; end 66 | if nargin < 2, res = 1; end 67 | 68 | % Get the figure size in pixels 69 | old_mode = get(fig, 'Units'); 70 | set(fig, 'Units', 'pixels'); 71 | px = get(fig, 'Position'); 72 | set(fig, 'Units', old_mode); 73 | 74 | % Retrieve the background colour 75 | bcol = get(fig, 'Color'); 76 | try 77 | % Try a direct Java screen-capture first - *MUCH* faster than print() to file 78 | % Note: we could also use A=matlab.graphics.internal.getframeWithDecorations(fig,false) but it (1) returns no alpha and (2) does not exist in older Matlabs 79 | if res == 1 80 | [A, alpha] = getJavaImage(fig); 81 | else 82 | error('magnify/downscale via print() to image file and then import'); 83 | end 84 | catch err %#ok 85 | % Warn if output is large 86 | npx = prod(px(3:4)*res)/1e6; 87 | if npx > 30 88 | % 30M pixels or larger! 89 | warning('MATLAB:LargeImage', 'print2array generating a %.1fM pixel image. This could be slow and might also cause memory problems.', npx); 90 | end 91 | % Set the resolution parameter 92 | res_str = ['-r' num2str(ceil(get(0, 'ScreenPixelsPerInch')*res))]; 93 | % Generate temporary file name 94 | tmp_nam = [tempname '.tif']; 95 | try 96 | % Ensure that the temp dir is writable (Javier Paredes 26/2/15) 97 | fid = fopen(tmp_nam,'w'); 98 | fwrite(fid,1); 99 | fclose(fid); 100 | delete(tmp_nam); % cleanup 101 | isTempDirOk = true; 102 | catch 103 | % Temp dir is not writable, so use the current folder 104 | [dummy,fname,fext] = fileparts(tmp_nam); %#ok 105 | fpath = pwd; 106 | tmp_nam = fullfile(fpath,[fname fext]); 107 | isTempDirOk = false; 108 | end 109 | % Enable users to specify optional ghostscript options (issue #36) 110 | if nargin > 3 && ~isempty(gs_options) 111 | if iscell(gs_options) 112 | gs_options = sprintf(' %s',gs_options{:}); 113 | elseif ~ischar(gs_options) 114 | error('gs_options input argument must be a string or cell-array of strings'); 115 | else 116 | gs_options = [' ' gs_options]; 117 | end 118 | else 119 | gs_options = ''; 120 | end 121 | if nargin > 2 && strcmp(renderer, '-painters') 122 | % First try to print directly to image file 123 | try 124 | % Print the file into a temporary image file and read it into array A 125 | [A, alpha, err, ex] = getPrintImage(fig, res_str, renderer, tmp_nam); 126 | if err, rethrow(ex); end 127 | catch % error - try to print to EPS and then using Ghostscript to TIF 128 | % Ensure that ghostscript() exists on the Matlab path 129 | if ~exist('ghostscript','file') && isempty(which('ghostscript')) 130 | error('export_fig:print2array:ghostscript', 'The ghostscript.m function is required by print2array.m. Install the complete export_fig package from https://www.mathworks.com/matlabcentral/fileexchange/23629-export_fig or https://github.com/altmany/export_fig') 131 | end 132 | % Print to eps file 133 | if isTempDirOk 134 | tmp_eps = [tempname '.eps']; 135 | else 136 | tmp_eps = fullfile(fpath,[fname '.eps']); 137 | end 138 | print2eps(tmp_eps, fig, 0, renderer, '-loose'); 139 | try 140 | % Initialize the command to export to tiff using ghostscript 141 | cmd_str = ['-dEPSCrop -q -dNOPAUSE -dBATCH ' res_str ' -sDEVICE=tiff24nc']; 142 | % Set the font path 143 | fp = font_path(); 144 | if ~isempty(fp) 145 | cmd_str = [cmd_str ' -sFONTPATH="' fp '"']; 146 | end 147 | % Add the filenames 148 | cmd_str = [cmd_str ' -sOutputFile="' tmp_nam '" "' tmp_eps '"' gs_options]; 149 | % Execute the ghostscript command 150 | ghostscript(cmd_str); 151 | catch me 152 | % Delete the intermediate file 153 | delete(tmp_eps); 154 | rethrow(me); 155 | end 156 | % Delete the intermediate file 157 | delete(tmp_eps); 158 | % Read in the generated bitmap 159 | A = imread(tmp_nam); 160 | % Delete the temporary bitmap file 161 | delete(tmp_nam); 162 | end 163 | else 164 | if nargin < 3 165 | renderer = '-opengl'; 166 | end 167 | % Print the file into a temporary image file and read it into array A 168 | [A, alpha, err, ex] = getPrintImage(fig, res_str, renderer, tmp_nam); 169 | % Throw any error that occurred 170 | if err 171 | % Display suggested workarounds to internal print() error (issue #16) 172 | fprintf(2, 'An error occured with Matlab''s builtin print function.\nTry setting the figure Renderer to ''painters'' or use opengl(''software'').\n\n'); 173 | rethrow(ex); 174 | end 175 | end 176 | end 177 | 178 | % Set the background color 179 | if isequal(bcol, 'none') 180 | bcol = squeeze(A(1,1,:)); 181 | if ~all(bcol==0) %if not black 182 | bcol = [255,255,255]; %=white %=[]; 183 | end 184 | else 185 | if all(bcol <= 1) 186 | bcol = bcol * 255; 187 | end 188 | if ~isequal(bcol, round(bcol)) 189 | bcol = squeeze(A(1,1,:)); 190 | %{ 191 | % Set border pixels to the correct colour 192 | for l = 1:size(A, 2) 193 | if ~all(reshape(A(:,l,:) == 255, [], 1)) 194 | break; 195 | end 196 | end 197 | for r = size(A, 2):-1:l 198 | if ~all(reshape(A(:,r,:) == 255, [], 1)) 199 | break; 200 | end 201 | end 202 | for t = 1:size(A, 1) 203 | if ~all(reshape(A(t,:,:) == 255, [], 1)) 204 | break; 205 | end 206 | end 207 | for b = size(A, 1):-1:t 208 | if ~all(reshape(A(b,:,:) == 255, [], 1)) 209 | break; 210 | end 211 | end 212 | bcol = median(single([reshape(A(:,[l r],:), [], size(A, 3)); ... 213 | reshape(A([t b],:,:), [], size(A, 3))]), 1)); 214 | for c = 1:size(A, 3) 215 | A(:,[1:l-1, r+1:end],c) = bcol(c); 216 | A([1:t-1, b+1:end],:,c) = bcol(c); 217 | end 218 | %} 219 | end 220 | end 221 | bcol = uint8(bcol); 222 | 223 | % Ensure that the output size is correct 224 | if isequal(res, round(res)) 225 | px = round([px([4 3])*res 3]); % round() to avoid an indexing warning below 226 | if any(size(A) > px) %~isequal(size(A), px) 227 | A = A(1:min(end,px(1)),1:min(end,px(2)),:); 228 | end 229 | if any(size(alpha) > px(1:2)) 230 | alpha = alpha(1:min(end,px(1)),1:min(end,px(2))); 231 | end 232 | end 233 | end 234 | 235 | % Get the Java-based screen-capture of the figure's JFrame content-panel 236 | function [imgData, alpha] = getJavaImage(hFig) 237 | % Get the figure's underlying Java frame 238 | oldWarn = warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame'); 239 | warning('off','MATLAB:ui:javaframe:PropertyToBeRemoved'); 240 | try 241 | jf = get(handle(hFig),'JavaFrame_I'); 242 | catch 243 | jf = get(handle(hFig),'JavaFrame'); %#ok 244 | end 245 | warning(oldWarn); 246 | 247 | % Get the Java frame's root frame handle 248 | %jframe = jf.getFigurePanelContainer.getComponent(0).getRootPane.getParent; 249 | try 250 | jClient = jf.fHG2Client; % This works from R2014b and up 251 | catch 252 | try 253 | jClient = jf.fHG1Client; % This works from R2008b-R2014a 254 | catch 255 | jClient = jf.fFigureClient; % This works up to R2011a 256 | end 257 | end 258 | 259 | % Get the content-pane 260 | try 261 | jPanel = jClient.getContentPane; 262 | catch 263 | jPanel = jClient.getFigurePanelContainer; 264 | end 265 | jPanel.repaint; 266 | w = jPanel.getWidth; 267 | h = jPanel.getHeight; 268 | 269 | % Create a BufferedImage and paint the content-pane into it 270 | % (https://coderanch.com/t/470601/java/screenshot-JPanel) 271 | % Note: contrary to documentation and common-sense, it turns out that TYPE_INT_RGB 272 | % ^^^^ returns non-opaque alpha, while TYPE_INT_ARGB only returns 255s in the alpha channel 273 | jOriginalGraphics = jPanel.getGraphics; 274 | import java.awt.image.BufferedImage 275 | try TYPE_INT_RGB = BufferedImage.TYPE_INT_RGB; catch, TYPE_INT_RGB = 1; end 276 | jImage = BufferedImage(w, h, TYPE_INT_RGB); 277 | jPanel.paint(jImage.createGraphics); 278 | jPanel.paint(jOriginalGraphics); % repaint original figure to avoid a blank window 279 | 280 | % Extract the RGB pixels from the BufferedImage (see screencapture.m) 281 | pixelsData = reshape(typecast(jImage.getData.getDataStorage, 'uint8'), 4, w, h); 282 | imgData = cat(3, ... 283 | transpose(reshape(pixelsData(3, :, :), w, h)), ... 284 | transpose(reshape(pixelsData(2, :, :), w, h)), ... 285 | transpose(reshape(pixelsData(1, :, :), w, h))); 286 | 287 | % And now also the alpha channel (if available) 288 | alpha = transpose(reshape(pixelsData(4, :, :), w, h)); 289 | 290 | % Ensure that the results are the expected size, otherwise raise an error 291 | figSize = getpixelposition(gcf); 292 | expectedSize = [figSize(4), figSize(3), 3]; 293 | if ~isequal(expectedSize, size(imgData)) 294 | error('bad Java screen-capture size!') 295 | end 296 | end 297 | 298 | % Export an image file of the figure using print() and then read it into an array 299 | function [imgData, alpha, err, ex] = getPrintImage(fig, res_str, renderer, tmp_nam) 300 | imgData = []; % fix for issue #254 301 | err = false; 302 | ex = []; 303 | alpha = []; 304 | % Temporarily set the paper size 305 | old_pos_mode = get(fig, 'PaperPositionMode'); 306 | old_orientation = get(fig, 'PaperOrientation'); 307 | set(fig, 'PaperPositionMode','auto', 'PaperOrientation','portrait'); 308 | try 309 | % Workaround for issue #69: patches with LineWidth==0.75 appear wide (internal bug in Matlab's print() function) 310 | fp = []; % in case we get an error below 311 | fp = findall(fig, 'Type','patch', 'LineWidth',0.75); 312 | set(fp, 'LineWidth',0.5); 313 | try %if using_hg2(fig) % HG2 (R2014b or newer) 314 | % Use print('-RGBImage') directly (a bit faster than via temp image file) 315 | imgData = print(fig, renderer, res_str, '-RGBImage'); 316 | catch %else % HG1 (R2014a or older) 317 | % Fix issue #83: use numeric handles in HG1 318 | fig = double(fig); 319 | % Print to image file 320 | print(fig, renderer, res_str, '-dtiff', tmp_nam); 321 | imgData = imread(tmp_nam); 322 | % Delete the temporary file 323 | delete(tmp_nam); 324 | end 325 | imgSize = size(imgData); imgSize = imgSize([1,2]); % Fix issue #308 326 | alpha = 255 * ones(imgSize, 'uint8'); % =all pixels opaque 327 | catch ex 328 | err = true; 329 | end 330 | set(fp, 'LineWidth',0.75); % restore original figure appearance 331 | % Reset the paper size 332 | set(fig, 'PaperPositionMode',old_pos_mode, 'PaperOrientation',old_orientation); 333 | end 334 | 335 | % Return (and create, where necessary) the font path (for use by ghostscript) 336 | function fp = font_path() 337 | fp = user_string('gs_font_path'); 338 | if ~isempty(fp) 339 | return 340 | end 341 | % Create the path 342 | % Start with the default path 343 | fp = getenv('GS_FONTPATH'); 344 | % Add on the typical directories for a given OS 345 | if ispc 346 | if ~isempty(fp) 347 | fp = [fp ';']; 348 | end 349 | fp = [fp getenv('WINDIR') filesep 'Fonts']; 350 | else 351 | if ~isempty(fp) 352 | fp = [fp ':']; 353 | end 354 | fp = [fp '/usr/share/fonts:/usr/local/share/fonts:/usr/share/fonts/X11:/usr/local/share/fonts/X11:/usr/share/fonts/truetype:/usr/local/share/fonts/truetype']; 355 | end 356 | user_string('gs_font_path', fp); 357 | end 358 | -------------------------------------------------------------------------------- /source_code/export_fig/read_write_entire_textfile.m: -------------------------------------------------------------------------------- 1 | %READ_WRITE_ENTIRE_TEXTFILE Read or write a whole text file to/from memory 2 | % 3 | % Read or write an entire text file to/from memory, without leaving the 4 | % file open if an error occurs. 5 | % 6 | % Reading: 7 | % fstrm = read_write_entire_textfile(fname) 8 | % Writing: 9 | % read_write_entire_textfile(fname, fstrm) 10 | % 11 | %IN: 12 | % fname - Pathname of text file to be read in. 13 | % fstrm - String to be written to the file, including carriage returns. 14 | % 15 | %OUT: 16 | % fstrm - String read from the file. If an fstrm input is given the 17 | % output is the same as that input. 18 | 19 | function fstrm = read_write_entire_textfile(fname, fstrm) 20 | modes = {'rt', 'wt'}; 21 | writing = nargin > 1; 22 | fh = fopen(fname, modes{1+writing}); 23 | if fh == -1 24 | error('Unable to open file %s.', fname); 25 | end 26 | try 27 | if writing 28 | fwrite(fh, fstrm, 'char*1'); 29 | else 30 | fstrm = fread(fh, '*char')'; 31 | end 32 | catch ex 33 | fclose(fh); 34 | rethrow(ex); 35 | end 36 | fclose(fh); 37 | end 38 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Extensions.type.Root/DependencyAnalysis.type.Extension/ExternalFiles.type.Extension.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/ProjectData.type.Info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/artifact.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/convenience.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/derived.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/design.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/none.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/other.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Categories/FileClassCategory.type.Category/test.type.Label.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/.gitignore.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/ImageSelection.class.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/ImageSelection.java.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/LICENSE.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/README.md.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/append_pdfs.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/copyfig.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/crop_borders.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/eps2pdf.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/export_fig.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/fix_lines.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/ghostscript.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/im2gif.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/isolate_axes.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/pdf2eps.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/pdftops.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/print2array.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/print2eps.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/read_write_entire_textfile.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/user_string.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.Files/using_hg2.m.type.File.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/Root.type.ProjectPath/a533e478-c90b-4a1f-a45b-f8877af57bee.type.Reference.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/resources/project/uuid-cc21a9b0-026d-4b70-9821-3eb23d2abf81.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source_code/export_fig/user_string.m: -------------------------------------------------------------------------------- 1 | function [string, file_name] = user_string(string_name, string) 2 | %USER_STRING Get/set a user specific string 3 | % 4 | % Examples: 5 | % string = user_string(string_name) 6 | % isSaved = user_string(string_name, new_string) 7 | % [value, file_name] = user_string(...) 8 | % 9 | % Function to get and set a string in a system or user specific file. This 10 | % enables, for example, system specific paths to binaries to be saved. 11 | % 12 | % The specified string will be saved in a file named .txt, 13 | % either in a subfolder named .ignore under this file's folder, or in the 14 | % user's prefdir folder (in case this file's folder is non-writable). 15 | % 16 | % IN: 17 | % string_name - String containing the name of the string required, which 18 | % sets the filename storing the string: .txt 19 | % new_string - The new string to be saved in the .txt file 20 | % 21 | % OUT: 22 | % string - The currently saved string. Default: '' 23 | % isSaved - Boolean indicating whether the save was succesful 24 | % file_name - path of the .txt file used to contain the requested string 25 | 26 | % Copyright (C) Oliver Woodford 2011-2014, Yair Altman 2015- 27 | 28 | % This method of saving paths avoids changing .m files which might be in a 29 | % version control system. Instead it saves the user dependent paths in 30 | % separate files with a .txt extension, which need not be checked in to 31 | % the version control system. Thank you to Jonas Dorn for suggesting this 32 | % approach. 33 | 34 | % 10/01/2013 - Access files in text, not binary mode, as latter can cause 35 | % errors. Thanks to Christian for pointing this out. 36 | % 29/05/2015 - Save file in prefdir if current folder is non-writable (issue #74) 37 | % 09/01/2018 - Fix issue #232: if the string looks like a file/folder path, ensure it actually exists 38 | % 15/01/2020 - Added file_name output argument 39 | 40 | if ~ischar(string_name) 41 | error('string_name must be a string.'); 42 | end 43 | % Create the full filename 44 | fname = [string_name '.txt']; 45 | dname = fullfile(fileparts(mfilename('fullpath')), '.ignore'); 46 | file_name = fullfile(dname, fname); 47 | default_file_name = file_name; 48 | if nargin > 1 49 | % Set string 50 | if ~ischar(string) 51 | error('new_string must be a string.'); 52 | end 53 | % Make sure the save directory exists 54 | %dname = fileparts(file_name); 55 | if ~exist(dname, 'dir') 56 | % Create the directory 57 | try 58 | if ~mkdir(dname) 59 | string = false; 60 | return 61 | end 62 | catch 63 | string = false; 64 | return 65 | end 66 | % Make it hidden 67 | try 68 | fileattrib(dname, '+h'); 69 | catch 70 | end 71 | end 72 | % Write the file 73 | fid = fopen(file_name, 'wt'); 74 | if fid == -1 75 | % file cannot be created/updated - use prefdir if file does not already exist 76 | % (if file exists but is simply not writable, don't create a duplicate in prefdir) 77 | if ~exist(file_name,'file') 78 | file_name = fullfile(prefdir, fname); 79 | fid = fopen(file_name, 'wt'); 80 | end 81 | if fid == -1 82 | string = false; 83 | file_name = default_file_name; 84 | return 85 | end 86 | end 87 | try 88 | fprintf(fid, '%s', string); 89 | catch 90 | fclose(fid); 91 | string = false; 92 | return 93 | end 94 | fclose(fid); 95 | string = true; 96 | else 97 | % Get string 98 | fid = fopen(file_name, 'rt'); 99 | if fid == -1 100 | % file cannot be read, try to read the file in prefdir 101 | file_name = fullfile(prefdir, fname); 102 | fid = fopen(file_name, 'rt'); 103 | if fid == -1 104 | string = ''; 105 | file_name = default_file_name; 106 | return 107 | end 108 | end 109 | string = fgetl(fid); 110 | fclose(fid); 111 | 112 | % Fix issue #232: if the string looks like a file/folder path, ensure it actually exists 113 | if ~isempty(string) && any(string=='\' | string=='/') && ~exist(string) %#ok 114 | string = ''; 115 | end 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /source_code/export_fig/using_hg2.m: -------------------------------------------------------------------------------- 1 | %USING_HG2 Determine if the HG2 graphics engine is used 2 | % 3 | % tf = using_hg2(fig) 4 | % 5 | %IN: 6 | % fig - handle to the figure in question. 7 | % 8 | %OUT: 9 | % tf - boolean indicating whether the HG2 graphics engine is being used 10 | % (true) or not (false). 11 | 12 | % 19/06/2015 - Suppress warning in R2015b; cache result for improved performance 13 | % 06/06/2016 - Fixed issue #156 (bad return value in R2016b) 14 | 15 | function tf = using_hg2(fig) 16 | persistent tf_cached 17 | if isempty(tf_cached) 18 | try 19 | if nargin < 1, fig = figure('visible','off'); end 20 | oldWarn = warning('off','MATLAB:graphicsversion:GraphicsVersionRemoval'); 21 | try 22 | % This generates a [supressed] warning in R2015b: 23 | tf = ~graphicsversion(fig, 'handlegraphics'); 24 | catch 25 | tf = ~verLessThan('matlab','8.4'); % =R2014b 26 | end 27 | warning(oldWarn); 28 | catch 29 | tf = false; 30 | end 31 | if nargin < 1, delete(fig); end 32 | tf_cached = tf; 33 | else 34 | tf = tf_cached; 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /source_code/plot_functions/create_plots.m: -------------------------------------------------------------------------------- 1 | function cycle_data = create_plots(x,fixed_parameters,filename_suffix) 2 | 3 | % Load setting for beautiful plots 4 | set_plot_options() 5 | 6 | % Evaluate the optimization problem 7 | fixed_parameters.calc_detail = 'long'; 8 | if nargin == 2 9 | filename = fixed_parameters.project_name; 10 | elseif nargin == 3 11 | filename = [fixed_parameters.project_name, '_', filename_suffix]; 12 | else 13 | error('The number of arguments must be 2 or 3') 14 | end 15 | filepath = fixed_parameters.results_path; 16 | cycle_data = evaluate_optimization_problem(x,fixed_parameters); 17 | 18 | % Choose whether to save the plots or not 19 | % 'choose_plots' is an structure that contains what diagrams to draw 20 | save = fixed_parameters.choose_plots.save; 21 | 22 | % Plot temperature vs heat flow rate diagram 23 | if strcmp(fixed_parameters.choose_plots.diagram_TQ,'yes') 24 | plot_TQ_diagram(cycle_data,filename,filepath,save) 25 | end 26 | 27 | % Plot temperature vs entropy diagram 28 | if strcmp(fixed_parameters.choose_plots.diagram_Ts,'yes') 29 | plot_Ts_diagram(cycle_data,filename,filepath,save) 30 | end 31 | 32 | % Plot temperature vs enthalpy diagram 33 | if strcmp(fixed_parameters.choose_plots.diagram_Th,'yes') 34 | plot_Th_diagram(cycle_data,filename,filepath,save) 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /source_code/plot_functions/plot_TQ_diagram.m: -------------------------------------------------------------------------------- 1 | function [] = plot_TQ_diagram(cycle_data,my_filename,my_path,save) 2 | %% Load the trajectories in the components 3 | % Evaporator 4 | h_evap_h = cycle_data.evaporator.h_h/1000; 5 | T_evap_h = cycle_data.evaporator.T_h-273.15; 6 | T_evap_c = cycle_data.evaporator.T_c-273.15; 7 | 8 | % Condenser 9 | h_cond_h = cycle_data.condenser.h_h/1000; 10 | T_cond_h = cycle_data.condenser.T_h-273.15; 11 | T_cond_c = cycle_data.condenser.T_c-273.15; 12 | 13 | % Recuperator 14 | h_rec_h = cycle_data.recuperator.h_h/1000; 15 | T_rec_h = cycle_data.recuperator.T_h-273.15; 16 | T_rec_c = cycle_data.recuperator.T_c-273.15; 17 | 18 | % Compute the heat flow rates 19 | m_h = cycle_data.mass_flows.m_h; 20 | m_f = cycle_data.mass_flows.m_f; 21 | q_evap = (h_evap_h-h_evap_h(1))*m_h; 22 | q_cond = (h_cond_h-h_cond_h(1))*m_f; 23 | q_rec = (h_rec_h-h_rec_h(1))*m_f; 24 | 25 | 26 | %% Plot the T-Q diagram 27 | % Prepare the figure 28 | fig = figure(); ax_fig = gca; 29 | hold on; box on; axis square; 30 | 31 | % Label de axes 32 | font_size = 12; 33 | xlabel({' '; '$\dot{Q}$ -- Heat transfer (kW)'},'FontSize',font_size) 34 | ylabel({'$T$ -- Temperature ($^\circ$C)';' '},'FontSize',font_size) 35 | ax_fig.XAxis.TickLabelFormat = '%.0f'; 36 | ax_fig.YAxis.TickLabelFormat = '%.0f'; 37 | 38 | plot(0,0,'r') 39 | plot(0,0,'k') 40 | plot(0,0,'b') 41 | 42 | plot(q_cond,T_cond_c,'b') 43 | plot(q_cond,T_cond_h,'k') 44 | plot((q_cond(end)+q_rec),T_rec_h,'k') 45 | plot((q_cond(end)+q_rec),T_rec_c,'k') 46 | plot((q_cond(end)+q_rec(end)+q_evap),T_evap_h,'r') 47 | plot((q_cond(end)+q_rec(end)+q_evap),T_evap_c,'k') 48 | 49 | x_lim = ax_fig.XLim; 50 | y_lim = ax_fig.YLim; 51 | 52 | plot([q_cond(end) q_cond(end)],[-10000 10000],'k','LineWidth',0.50) 53 | plot([q_cond(end)+q_rec(end) q_cond(end)+q_rec(end)],[-10000 10000],'k','LineWidth',0.50) 54 | plot([q_cond(end)+q_rec(end)+q_evap(end) q_cond(end)+q_rec(end)+q_evap(end)],[-10000 10000],'k','LineWidth',0.50) 55 | 56 | ax_fig.XLim = x_lim; 57 | ax_fig.YLim = y_lim; 58 | 59 | % Save the figure 60 | name = 'TQ_diagram'; 61 | if save == 1 62 | saveas(fig,fullfile(my_path,[my_filename,'_',name,'.pdf']),'pdf') 63 | elseif save == 2 64 | % saveas(fig,fullfile(my_path,[my_filename,'_',name]),'fig') 65 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-png','-r1000') 66 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-eps','-painters') 67 | export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-pdf','-painters') 68 | elseif save ~= 0 69 | error('Choose a valid saving option') 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /source_code/plot_functions/plot_Th_diagram.m: -------------------------------------------------------------------------------- 1 | function [] = plot_Th_diagram(cycle_data,my_filename,my_path,save) 2 | %% Load the cycle points 3 | h_cycle = [cycle_data.cycle_states.h]/1000; 4 | T_cycle = [cycle_data.cycle_states.T]-273.15; 5 | working_fluid = cycle_data.fluids.working_fluid; 6 | T_min = cycle_data.properties.T_min-273.15; 7 | T_trip = cycle_data.properties.T_trip-273.15; 8 | T_crit = cycle_data.properties.T_crit-273.15; 9 | 10 | 11 | %% Load the trajectories in the components 12 | % Evaporator 13 | h_evap_c = cycle_data.evaporator.h_c/1000; 14 | T_evap_c = cycle_data.evaporator.T_c-273.15; 15 | T_evap_h = cycle_data.evaporator.T_h-273.15; 16 | 17 | % Condenser 18 | h_cond_h = cycle_data.condenser.h_h/1000; 19 | T_cond_h = cycle_data.condenser.T_h-273.15; 20 | T_cond_c = cycle_data.condenser.T_c-273.15; 21 | 22 | % Recuperator 23 | h_rec_c = cycle_data.recuperator.h_c/1000; 24 | h_rec_h = cycle_data.recuperator.h_h/1000; 25 | T_rec_c = cycle_data.recuperator.T_c-273.15; 26 | T_rec_h = cycle_data.recuperator.T_h-273.15; 27 | 28 | % Expander 29 | h_exp = cycle_data.expander.h/1000; 30 | T_exp = cycle_data.expander.T-273.15; 31 | 32 | % Pump 33 | h_pump = cycle_data.pump_f.h/1000; 34 | T_pump = cycle_data.pump_f.T-273.15; 35 | 36 | 37 | %% Plot the T-h diagram 38 | % Prepare the figure 39 | fig = figure(); ax_fig = gca; 40 | hold on; box on; 41 | pbaspect([1 1 1]) 42 | % axis square; 43 | 44 | % Define the axis limits 45 | TT = T_cycle(1:end); 46 | hh = h_cycle(4:9); 47 | T_minplot = min(0.90*T_min,min(TT)-(max(TT)-min(TT))/8); 48 | T_maxplot = max(1.10*T_crit,max(TT)+(max(TT)-min(TT))/8); 49 | if T_min < T_crit 50 | [~, h_sat] = sat_line(working_fluid,T_min+273.15,T_crit+273.15,'T','H',200); h_sat = h_sat/1000; 51 | h_minplot = min(h_sat(1)-(h_sat(end)-h_sat(1))/8,min(hh)-(max(hh)-min(hh))/8); 52 | h_maxplot = max(1.2*max(h_sat),max(hh)+(max(hh)-min(hh))/8); 53 | else 54 | h_minplot = min(hh)-(max(hh)-min(hh))/8; 55 | h_maxplot = max(hh)+(max(hh)-min(hh))/8; 56 | end 57 | 58 | axis([h_minplot h_maxplot T_minplot T_maxplot]) 59 | 60 | % Label de axes 61 | font_size = 12; 62 | xlabel({' ';'$h$ -- Enthalpy (kJ/kg)'},'FontSize',font_size); 63 | ylabel({'$T$ -- Temperature ($^\circ$C)';' '},'FontSize',font_size); 64 | ax_fig.YAxis.TickLabelFormat = '%.0f'; 65 | ax_fig.XAxis.TickLabelFormat = '%.0f'; 66 | 67 | % Plot theaturation line 68 | [T_sat, h_sat] = sat_line(working_fluid,T_trip+273.15,T_crit+273.15,'T','H',200); 69 | T_sat = T_sat-273.15; h_sat = h_sat/1000; 70 | plot(h_sat,T_sat,'k','LineWidth',0.5) 71 | 72 | % Plot the thermodynamic trajectories in the components 73 | plot(h_pump,T_pump,'k') 74 | plot(h_exp,T_exp,'k') 75 | plot(h_rec_h,T_rec_h,'k') 76 | plot(h_rec_c,T_rec_c,'k') 77 | plot(h_evap_c,T_evap_c,'k') 78 | plot(h_cond_h,T_cond_h,'k') 79 | plot(h_evap_c,T_evap_h,'r') 80 | plot(h_cond_h,T_cond_c,'b') 81 | 82 | % Plot the cycle points 83 | plot(h_cycle(4:9),T_cycle(4:9),'ko','MarkerFaceColor','k','MarkerSize',2) 84 | plot(h_cycle([7,6,6]),T_cycle([1,2,3]),'ro','MarkerFaceColor','r','MarkerSize',2) 85 | plot(h_cycle([4,4,9]),T_cycle([10,11,12]),'bo','MarkerFaceColor','b','MarkerSize',2) 86 | plot(h_cycle([6,6]),T_cycle([2,3]),'r-') 87 | plot(h_cycle([4,4]),T_cycle([10,11]),'b-') 88 | 89 | % Label the cycle points 90 | p1 = '$\quad 1$'; text(h_cycle(7),T_cycle(1), p1, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 91 | p2 = '$2 \quad$'; text(h_cycle(6),T_cycle(2)-3, p2, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 92 | p3 = '$3 \quad$'; text(h_cycle(6),T_cycle(3)+3, p3, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 93 | p4 = '$4 \quad$'; text(h_cycle(4),T_cycle(4)-3, p4, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 94 | p5 = '$5 \quad$'; text(h_cycle(5),T_cycle(5)+3, p5, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 95 | p6 = '$6 \quad$'; text(h_cycle(6),T_cycle(6)+6, p6, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 96 | p7 = '$\quad 7$'; text(h_cycle(7),T_cycle(7), p7, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 97 | p8 = '$\quad 8$'; text(h_cycle(8),T_cycle(8)+3, p8, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 98 | p9 = '$\quad 9$'; text(h_cycle(9),T_cycle(9)-3, p9, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 99 | p10 = '$10 \quad$'; text(h_cycle(4),T_cycle(10)-3, p10, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 100 | p11 = '$11 \quad$'; text(h_cycle(4),T_cycle(11)+3, p11, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 101 | p12 = '$\quad 12$'; text(h_cycle(9),T_cycle(12), p12, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 102 | 103 | % Save the figure 104 | name = 'Th_diagram'; 105 | if save == 1 106 | saveas(fig,fullfile(my_path,[my_filename,'_',name,'.pdf']),'pdf') 107 | elseif save == 2 108 | % saveas(fig,fullfile(my_path,[my_filename,'_',name]),'fig') 109 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-png','-r1000') 110 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-eps','-painters') 111 | export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-pdf','-painters') 112 | elseif save ~= 0 113 | error('Choose a valid saving option') 114 | end 115 | 116 | end 117 | -------------------------------------------------------------------------------- /source_code/plot_functions/plot_Ts_diagram.m: -------------------------------------------------------------------------------- 1 | function [] = plot_Ts_diagram(cycle_data,my_filename,my_path,save) 2 | %% Load the cycle points 3 | s_cycle = [cycle_data.cycle_states.s]/1000; 4 | T_cycle = [cycle_data.cycle_states.T]-273.15; 5 | working_fluid = cycle_data.fluids.working_fluid; 6 | T_min = cycle_data.properties.T_min-273.15; 7 | T_trip = cycle_data.properties.T_trip-273.15; 8 | T_crit = cycle_data.properties.T_crit-273.15; 9 | 10 | 11 | %% Load the trajectories in the components 12 | % Evaporator 13 | s_evap_c = cycle_data.evaporator.s_c/1000; 14 | T_evap_c = cycle_data.evaporator.T_c-273.15; 15 | T_evap_h = cycle_data.evaporator.T_h-273.15; 16 | 17 | % Condenser 18 | s_cond_h = cycle_data.condenser.s_h/1000; 19 | T_cond_h = cycle_data.condenser.T_h-273.15; 20 | T_cond_c = cycle_data.condenser.T_c-273.15; 21 | 22 | % Recuperator 23 | s_rec_c = cycle_data.recuperator.s_c/1000; 24 | s_rec_h = cycle_data.recuperator.s_h/1000; 25 | T_rec_c = cycle_data.recuperator.T_c-273.15; 26 | T_rec_h = cycle_data.recuperator.T_h-273.15; 27 | 28 | % Expander 29 | s_exp = cycle_data.expander.s/1000; 30 | T_exp = cycle_data.expander.T-273.15; 31 | 32 | % Pump 33 | s_pump = cycle_data.pump_f.s/1000; 34 | T_pump = cycle_data.pump_f.T-273.15; 35 | 36 | 37 | %% Plot the T-s diagram 38 | % Prepare the figure 39 | fig = figure(); ax_fig = gca; 40 | hold on; box on; 41 | pbaspect([1 1 1]) 42 | % axis square; 43 | 44 | % Define the axis limits 45 | TT = T_cycle(1:end); 46 | ss = s_cycle(4:9); 47 | T_minplot = min(0.90*T_min,min(TT)-(max(TT)-min(TT))/8); 48 | T_maxplot = max(1.10*T_crit,max(TT)+(max(TT)-min(TT))/8); 49 | if T_min < T_crit 50 | [~, s_sat] = sat_line(working_fluid,T_min+273.15,T_crit+273.15,'T','S',200); s_sat = s_sat/1000; 51 | s_minplot = min(s_sat(1)-(s_sat(end)-s_sat(1))/8,min(ss)-(max(ss)-min(ss))/8); 52 | s_maxplot = max(1.2*max(s_sat),max(ss)+(max(ss)-min(ss))/8); 53 | else 54 | s_minplot = min(ss)-(max(ss)-min(ss))/8; 55 | s_maxplot = max(ss)+(max(ss)-min(ss))/8; 56 | end 57 | axis([s_minplot s_maxplot T_minplot T_maxplot]) 58 | 59 | % Label de axes 60 | font_size = 12; 61 | xlabel({' ';'$s$ -- Entropy (kJ/kg$\,$K)'},'FontSize',font_size); 62 | ylabel({'$T$ -- Temperature ($^\circ$C)';' '},'FontSize',font_size); 63 | ax_fig.YAxis.TickLabelFormat = '%.0f'; 64 | ax_fig.XAxis.TickLabelFormat = '%.2f'; 65 | 66 | % Plot theaturation line 67 | [T_sat, s_sat] = sat_line(working_fluid,T_trip+273.15,T_crit+273.15,'T','S',200); 68 | T_sat = T_sat-273.15; s_sat = s_sat/1000; 69 | plot(s_sat,T_sat,'k','LineWidth',0.5) 70 | 71 | % Plot the thermodynamic trajectories in the components 72 | plot(s_pump,T_pump,'k') 73 | plot(s_exp,T_exp,'k') 74 | plot(s_rec_h,T_rec_h,'k') 75 | plot(s_rec_c,T_rec_c,'k') 76 | plot(s_cond_h,T_cond_h,'k') 77 | plot(s_evap_c,T_evap_c,'k') 78 | plot(s_cond_h,T_cond_h,'k') 79 | plot(s_evap_c,T_evap_h,'r') 80 | plot(s_cond_h,T_cond_c,'b') 81 | 82 | % Plot the cycle points 83 | plot(s_cycle(4:9),T_cycle(4:9),'ko','MarkerFaceColor','k','MarkerSize',2) 84 | plot(s_cycle([7,6,6]),T_cycle([1,2,3]),'ro','MarkerFaceColor','r','MarkerSize',2) 85 | plot(s_cycle([4,4,9]),T_cycle([10,11,12]),'bo','MarkerFaceColor','b','MarkerSize',2) 86 | plot(s_cycle([6,6]),T_cycle([2,3]),'r-') 87 | plot(s_cycle([4,4]),T_cycle([10,11]),'b-') 88 | 89 | % Label the cycle points 90 | p1 = '$\quad 1$'; text(s_cycle(7),T_cycle(1), p1, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 91 | p2 = '$2 \quad$'; text(s_cycle(6),T_cycle(2)-3, p2, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 92 | p3 = '$3 \quad$'; text(s_cycle(6),T_cycle(3)+3, p3, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 93 | p4 = '$4 \quad$'; text(s_cycle(4),T_cycle(4)-3, p4, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 94 | p5 = '$5 \quad$'; text(s_cycle(5),T_cycle(5)+3, p5, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 95 | p6 = '$6 \quad$'; text(s_cycle(6),T_cycle(6)+6, p6, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 96 | p7 = '$\quad 7$'; text(s_cycle(7),T_cycle(7), p7, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 97 | p8 = '$\quad 8$'; text(s_cycle(8),T_cycle(8)+3, p8, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 98 | p9 = '$\quad 9$'; text(s_cycle(9),T_cycle(9)-3, p9, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 99 | p10 = '$10 \quad$'; text(s_cycle(4),T_cycle(10)-3, p10, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 100 | p11 = '$11 \quad$'; text(s_cycle(4),T_cycle(11)+3, p11, 'HorizontalAlignment', 'right', 'FontSize', font_size-2) 101 | p12 = '$\quad 12$'; text(s_cycle(9),T_cycle(12), p12, 'HorizontalAlignment', 'left', 'FontSize', font_size-2) 102 | 103 | % Save the figure 104 | name = 'Ts_diagram'; 105 | if save == 1 106 | saveas(fig,fullfile(my_path,[my_filename,'_',name,'.pdf']),'pdf') 107 | elseif save == 2 108 | % saveas(fig,fullfile(my_path,[my_filename,'_',name]),'fig') 109 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-png','-r1000') 110 | % export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-eps','-painters') 111 | export_fig(fig,fullfile(my_path,[my_filename,'_',name]),'-pdf','-painters') 112 | elseif save ~= 0 113 | error('Choose a valid saving option') 114 | end 115 | 116 | end 117 | -------------------------------------------------------------------------------- /source_code/plot_functions/set_plot_options.m: -------------------------------------------------------------------------------- 1 | function set_plot_options 2 | %% Printing options 3 | FontName = 'monospace'; 4 | FontSize = 11; 5 | AxLineWidth = 0.75; 6 | LineWidth = 1.00; 7 | MarkerSize = 5; 8 | 9 | % Setting LaTeX as text interpreter 10 | set(groot,'DefaultTextInterpreter','latex', ... 11 | 'DefaultAxesTickLabelInterpreter','latex', ... 12 | 'DefaultLegendInterpreter','latex'); 13 | 14 | % Setting several default options for the graphics 15 | set(groot, 'DefaultFigureColor','White' , ... 16 | 'DefaultFigurePaperType', 'a4letter', ... 17 | 'DefaultAxesColor', 'white', ... 18 | 'DefaultAxesFontUnits', 'points',... 19 | 'DefaultAxesFontSize', FontSize, ... 20 | 'DefaultAxesFontAngle', 'normal', ... 21 | 'DefaultAxesGridLineStyle', '-', ... 22 | 'DefaultAxesGridAlpha', 0.25, ... 23 | 'DefaultAxesGridColor', [0, 0, 0], ... 24 | 'DefaultAxesInterruptible', 'on', ... 25 | 'DefaultAxesLayer', 'Bottom', ... 26 | 'DefaultAxesNextPlot', 'replace', ... 27 | 'DefaultAxesUnits', 'normalized', ... 28 | 'DefaultAxesXcolor', [0, 0, 0], ... 29 | 'DefaultAxesYcolor', [0, 0, 0], ... 30 | 'DefaultAxesZcolor', [0, 0, 0], ... 31 | 'DefaultAxesVisible', 'on', ... 32 | 'DefaultAxesLineWidth', AxLineWidth, ... 33 | 'DefaultLineLineWidth', LineWidth, ... 34 | 'DefaultLineMarkerSize', MarkerSize, ... 35 | 'DefaultTextColor', [0, 0, 0], ... 36 | 'DefaultTextFontUnits', 'Points', ... 37 | 'DefaultTextFontName', FontName, ... % Not applied when LaTeX is used 38 | 'DefaultAxesFontName', FontName, ... % Not applied when LaTeX is used 39 | 'DefaultTextFontSize', FontSize, ... 40 | 'DefaultTextVerticalAlignment', 'middle', ... 41 | 'DefaultTextHorizontalAlignment', 'left') 42 | 43 | % Setting a position for the figure 44 | set(groot,'DefaultFigurePosition', [360 198 560 420]); 45 | 46 | end 47 | -------------------------------------------------------------------------------- /source_code/print_solution.m: -------------------------------------------------------------------------------- 1 | function [] = print_solution(cycle_data) 2 | 3 | % Print bounds 4 | bounds_summary = cycle_data.optimization.bounds_summary; 5 | header = bounds_summary.header; 6 | name = bounds_summary.name; 7 | value = bounds_summary.value; 8 | value_min = bounds_summary.value_min; 9 | value_max = bounds_summary.value_max; 10 | satisfied = bounds_summary.satisfied; 11 | active = bounds_summary.active; 12 | fprintf('\n') 13 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 14 | fprintf('|---------------------------------- Values and bounds of the design variables ----------------------------------|\n') 15 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 16 | fprintf('%31s %15s %12s %15s %12s %10s \n', header{1}, header{2}, header{3}, header{4}, header{5}, header{6}); 17 | for i = 1:size(name,2) 18 | fprintf('%31s %15.4f %12.4f %15.4f %12s %10s \n', name{i}, value_min{i}, value{i}, value_max{i}, satisfied{i}, active{i}); 19 | end 20 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 21 | 22 | % Print constraints 23 | constraint_summary = cycle_data.optimization.constraint_summary; 24 | header = constraint_summary.header; 25 | name = constraint_summary.name; 26 | value = constraint_summary.value; 27 | value_min = constraint_summary.value_min; 28 | value_max = constraint_summary.value_max; 29 | applied = constraint_summary.applied; 30 | satisfied = constraint_summary.satisfied; 31 | active = constraint_summary.active; 32 | fprintf('\n') 33 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 34 | fprintf('|--------------------------------- Values and limits of the problem constraints --------------------------------|\n') 35 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 36 | fprintf('%31s %15s %12s %15s %10s %12s %10s \n', header{1}, header{2}, header{3}, header{4}, header{5}, header{6}, header{7}); 37 | for i = 1:size(name,2) 38 | fprintf('%31s %15.4f %12.4f %15.4f %10s %12s %10s \n', name{i}, value_min{i}, value{i}, value_max{i}, applied{i}, satisfied{i}, active{i}); 39 | end 40 | fprintf('|---------------------------------------------------------------------------------------------------------------|\n') 41 | 42 | end -------------------------------------------------------------------------------- /source_code/property_functions/cluster_func.m: -------------------------------------------------------------------------------- 1 | function f = cluster_func(z,beta) 2 | % Muller introduced this function in his notes (I should find a reference) 3 | % 01 is the clustering parameter 5 | 6 | f = 1 + beta*(1-((beta+1)/(beta-1)).^(1-z))./(1+((beta+1)/(beta-1)).^(1-z)); 7 | 8 | % The cluster function puts more points in the region where z is close to 0 9 | % If you want more points in the region where z is close to 1 define the 10 | % problem in the opposite manner and then reverse the resulting vector 11 | 12 | end -------------------------------------------------------------------------------- /source_code/property_functions/liq_line.m: -------------------------------------------------------------------------------- 1 | function [X_liq, Y_liq] = liq_line(fluid,T_min,T_max,x,y,N_liq) 2 | 3 | % Clustering parameter (>1) 4 | beta = 1.0010; 5 | 6 | % Liquid saturation line 7 | z_liq = linspace(0,1,N_liq)'; 8 | T_liq = T_max+cluster_func(z_liq,beta)*(T_min-T_max); 9 | T_liq(1:end) = T_liq(end:-1:1); % Reverse the vector (clustering) 10 | X_liq = zeros(N_liq,1); % Pre-allocate space 11 | Y_liq = zeros(N_liq,1); % Pre-allocate space 12 | for i = 1:N_liq 13 | X_liq(i) = prop_calculation(x,'T',T_liq(i),'Q',0,fluid); 14 | Y_liq(i) = prop_calculation(y,'T',T_liq(i),'Q',0,fluid); 15 | end 16 | 17 | end -------------------------------------------------------------------------------- /source_code/property_functions/prop_calculation.m: -------------------------------------------------------------------------------- 1 | function prop = prop_calculation(varargin) 2 | 3 | % CoolProp function call 4 | prop = py.CoolProp.CoolProp.PropsSI(varargin{:}); 5 | 6 | % REFPROP function call 7 | % prop = refpropm(varargin{:}); 8 | 9 | end -------------------------------------------------------------------------------- /source_code/property_functions/quality.m: -------------------------------------------------------------------------------- 1 | function q = quality(prop2,prop2_value,fluid,p,p_crit) 2 | 3 | % Give an output for quality even if the pressure is supercritical 4 | if p < p_crit 5 | q = prop_calculation('Q','P',p,prop2,prop2_value,fluid); 6 | elseif p>= p_crit 7 | q = 1.1; 8 | else 9 | error('Ooops, something went wrong in quality computation'); 10 | end 11 | 12 | if q < 0 13 | q = 1.1; 14 | 15 | end -------------------------------------------------------------------------------- /source_code/property_functions/sat_line.m: -------------------------------------------------------------------------------- 1 | function [X_sat, Y_sat] = sat_line(fluid,T_min,T_max,x,y,N) 2 | 3 | % Compute the liquid and the vapor saturation lines 4 | [X_liq, Y_liq] = liq_line(fluid,T_min,T_max,x,y,ceil(N/2)); 5 | [X_vap, Y_vap] = vap_line(fluid,T_min,T_max,x,y,floor(N/2)); 6 | 7 | % Export the results 8 | X_sat = [X_liq; X_vap]; % Create a single vector with property x 9 | Y_sat = [Y_liq; Y_vap]; % Create a single vector with property y 10 | 11 | end -------------------------------------------------------------------------------- /source_code/property_functions/vap_line.m: -------------------------------------------------------------------------------- 1 | function [X_vap, Y_vap] = vap_line(fluid,T_min,T_max,x,y,N_vap) 2 | 3 | % Clustering parameter (>1) 4 | beta = 1.0010; 5 | 6 | % Vapor saturation line 7 | z_vap = linspace(0,1,N_vap)'; 8 | T_vap = T_max+cluster_func(z_vap,beta)*(T_min-T_max); 9 | X_vap = zeros(N_vap,1); % Pre-allocate space 10 | Y_vap = zeros(N_vap,1); % Pre-allocate space 11 | for i = 1:N_vap 12 | X_vap(i) = prop_calculation(x,'T',T_vap(i),'Q',1,fluid); 13 | Y_vap(i) = prop_calculation(y,'T',T_vap(i),'Q',1,fluid); 14 | end 15 | 16 | end -------------------------------------------------------------------------------- /source_code/save_current_solution.m: -------------------------------------------------------------------------------- 1 | function stop = save_current_solution(x,~,~,fixed_parameters) 2 | 3 | % Use a persistent variable to keep track of the number of iterations 4 | persistent iter 5 | if isempty(iter) 6 | iter = 0; 7 | end 8 | iter = iter+1; 9 | 10 | % Evaluate the cycle model for the current vector of independent variables 11 | fixed_parameters.calc_detail = 'short'; 12 | cycle_data = evaluate_rankine_cycle(x,fixed_parameters); 13 | 14 | % Save the current solution as a MATLAB data structure 15 | save(fullfile(fixed_parameters.results_path,[fixed_parameters.project_name, '_', num2str(iter,'%02d'), '.mat']),'cycle_data') 16 | 17 | % Return a false stop flag 18 | stop = false; 19 | 20 | end -------------------------------------------------------------------------------- /source_code/save_solution.m: -------------------------------------------------------------------------------- 1 | function [] = save_solution(cycle_data,my_filename,my_path) 2 | 3 | % Print optimization problem output 4 | file_name = fopen(fullfile(my_path,[my_filename,'_solution_optimization.txt']), 'w'); 5 | bounds_summary = cycle_data.optimization.bounds_summary; 6 | header = bounds_summary.header; 7 | name = bounds_summary.name; 8 | value = bounds_summary.value; 9 | value_min = bounds_summary.value_min; 10 | value_max = bounds_summary.value_max; 11 | satisfied = bounds_summary.satisfied; 12 | active = bounds_summary.active; 13 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 14 | fprintf(file_name,'|---------------------------------- Values and bounds of the design variables ----------------------------------|\n'); 15 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 16 | fprintf(file_name,'%31s %15s %12s %15s %12s %10s \n', header{1}, header{2}, header{3}, header{4}, header{5}, header{6}); 17 | for i = 1:size(name,2) 18 | fprintf(file_name,'%31s %15.4f %12.4f %15.4f %12s %10s \n', name{i}, value_min{i}, value{i}, value_max{i}, satisfied{i}, active{i}); 19 | end 20 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 21 | 22 | constraint_summary = cycle_data.optimization.constraint_summary; 23 | header = constraint_summary.header; 24 | name = constraint_summary.name; 25 | value = constraint_summary.value; 26 | value_min = constraint_summary.value_min; 27 | value_max = constraint_summary.value_max; 28 | applied = constraint_summary.applied; 29 | satisfied = constraint_summary.satisfied; 30 | active = constraint_summary.active; 31 | fprintf(file_name,'\n'); 32 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 33 | fprintf(file_name,'|--------------------------------- Values and limits of the problem constraints --------------------------------|\n'); 34 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 35 | fprintf(file_name,'%31s %15s %12s %15s %10s %12s %10s \n', header{1}, header{2}, header{3}, header{4}, header{5}, header{6}, header{7}); 36 | for i = 1:size(name,2) 37 | fprintf(file_name,'%31s %15.4f %12.4f %15.4f %10s %12s %10s \n', name{i}, value_min{i}, value{i}, value_max{i}, applied{i}, satisfied{i}, active{i}); 38 | end 39 | fprintf(file_name,'|---------------------------------------------------------------------------------------------------------------|\n'); 40 | fclose(file_name); 41 | 42 | end -------------------------------------------------------------------------------- /source_code/solve_optimization_problem.m: -------------------------------------------------------------------------------- 1 | function [cycle_data,x_opt,f_opt,exitflag,output,lambda] = solve_optimization_problem(fixed_parameters,optimization_problem) 2 | 3 | % This function is used to have a temporal storage of the objective 4 | % function and the constraints and avoid the evaluation of the model 5 | % each time the objective function or the constraints are required 6 | % (this function might look complex, but it is just a trick to speed the 7 | % optimization speed by a factor of 2) 8 | 9 | x_last = []; % Degrees of freedom in the last computation 10 | f_bis = []; % Objective function in storage 11 | c_bis = []; % Nonlinear inequality constraints in storage 12 | c_eq_bis = []; % Nonlinear equality constraints in storage 13 | 14 | % Objective function and nonlinear constraints functions (nested below) 15 | fixed_parameters.calc_detail = 'short'; 16 | optimization_problem.objective = @(x) evaluate_objective_function(x,fixed_parameters); 17 | optimization_problem.nonlcon = @(x) evaluate_constraints(x,fixed_parameters); 18 | 19 | % Use fmincon to solve the optimization problem 20 | tic 21 | [x_opt,f_opt,exitflag,output,lambda] = fmincon(optimization_problem); 22 | cycle_data = evaluate_optimization_problem(x_opt,fixed_parameters); 23 | t = toc; 24 | disp(['The optimization problem was solved in ', num2str(t), ' seconds']) 25 | disp(['The optimization exit flag is ', num2str(exitflag)]) 26 | 27 | 28 | function f = evaluate_objective_function(x,fixed_parameters) 29 | if ~isequal(x,x_last) % Check if computation is necessary 30 | [~,f_bis,c_bis,c_eq_bis] = evaluate_optimization_problem(x,fixed_parameters); 31 | x_last = x; 32 | end 33 | f = f_bis; 34 | end 35 | 36 | function [c, c_eq] = evaluate_constraints(x,fixed_parameters) 37 | if ~isequal(x,x_last) % Check if computation is necessary 38 | [~,f_bis,c_bis,c_eq_bis] = evaluate_optimization_problem(x,fixed_parameters); 39 | x_last = x; 40 | end 41 | c = c_bis; 42 | c_eq = c_eq_bis; 43 | end 44 | 45 | 46 | end 47 | 48 | --------------------------------------------------------------------------------